pgkiln 0.62.0 → 0.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SECURITY.md +4 -0
- package/db/migrations/131_builder_sso.sql +69 -0
- package/db/migrations/132_app_user_mfa.sql +58 -0
- package/docs/apex-feature-parity.md +5 -2
- package/docs/guide/03-builder.md +46 -0
- package/docs/guide/08-security.md +48 -6
- package/docs/guide/09-reference.md +5 -1
- package/docs/guide/11-from-apex.md +2 -1
- package/docs/guide/12-development.md +9 -0
- package/docs/guide/18-cli.md +1 -0
- package/package.json +1 -1
- package/public/app.css +10 -0
- package/public/builder.css +4 -0
- package/src/account-mfa.ts +93 -0
- package/src/builder/advisor.ts +8 -0
- package/src/builder/builder-sso.ts +135 -0
- package/src/builder/i18n/de.ts +56 -0
- package/src/builder/i18n/en.ts +56 -0
- package/src/builder/i18n/es.ts +56 -0
- package/src/builder/i18n/fr.ts +56 -0
- package/src/builder/i18n/nl.ts +56 -0
- package/src/builder/i18n/pt.ts +56 -0
- package/src/builder/ldap.ts +2 -0
- package/src/builder/login-page.ts +26 -2
- package/src/builder/routes.ts +137 -21
- package/src/builder/security.ts +3 -1
- package/src/builder/ui.ts +2 -1
- package/src/builder/users.ts +36 -3
- package/src/builder/workspaces.ts +37 -3
- package/src/cli/main.ts +19 -5
- package/src/devsso.ts +149 -0
- package/src/i18n/ar.ts +34 -0
- package/src/i18n/cs.ts +34 -0
- package/src/i18n/da.ts +34 -0
- package/src/i18n/de.ts +34 -0
- package/src/i18n/el.ts +34 -0
- package/src/i18n/es.ts +34 -0
- package/src/i18n/fi.ts +34 -0
- package/src/i18n/fr.ts +34 -0
- package/src/i18n/he.ts +34 -0
- package/src/i18n/it.ts +34 -0
- package/src/i18n/ja.ts +34 -0
- package/src/i18n/ko.ts +34 -0
- package/src/i18n/nb.ts +34 -0
- package/src/i18n/pl.ts +34 -0
- package/src/i18n/pt.ts +34 -0
- package/src/i18n/ru.ts +34 -0
- package/src/i18n/sv.ts +34 -0
- package/src/i18n/tr.ts +34 -0
- package/src/i18n/uk.ts +34 -0
- package/src/i18n/zh.ts +34 -0
- package/src/i18n.ts +68 -0
- package/src/metadata.ts +4 -1
- package/src/mfa.ts +76 -52
- package/src/remember.ts +1 -1
- package/src/runtime/account.ts +4 -1
- package/src/runtime/routes.ts +24 -9
- package/src/runtime/two-step.ts +395 -0
- package/src/saml.ts +13 -7
- package/src/sharedlogin.ts +1 -1
- package/src/sso.ts +37 -7
package/src/sso.ts
CHANGED
|
@@ -34,6 +34,12 @@ export interface Provider {
|
|
|
34
34
|
protocol: 'oidc' | 'saml';
|
|
35
35
|
idp_sso_url: string | null;
|
|
36
36
|
idp_cert: string | null;
|
|
37
|
+
/** (131) developers sign in to the builder with this provider too (devsso.ts) */
|
|
38
|
+
builder: boolean;
|
|
39
|
+
builder_auto_create: boolean;
|
|
40
|
+
builder_link_existing: boolean;
|
|
41
|
+
builder_group: string | null;
|
|
42
|
+
builder_admin_group: string | null;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
interface Discovery {
|
|
@@ -86,8 +92,11 @@ function jwks(doc: Discovery) {
|
|
|
86
92
|
return set;
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
/**
|
|
90
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Start: returns the provider URL to redirect to, and the browser-binding cookie value.
|
|
97
|
+
* appId null: a sign-in to the builder; link: the signed-in developer linking this identity.
|
|
98
|
+
*/
|
|
99
|
+
export async function startSignIn(p: Provider, appId: number | null, next: string | undefined, link?: string) {
|
|
91
100
|
const doc = await discover(p.issuer);
|
|
92
101
|
const state = b64url(randomBytes(32));
|
|
93
102
|
const nonce = b64url(randomBytes(32));
|
|
@@ -95,9 +104,9 @@ export async function startSignIn(p: Provider, appId: number, next: string | und
|
|
|
95
104
|
const browserKey = b64url(randomBytes(32));
|
|
96
105
|
await owner.query(`delete from meta.sso_pending where created_at < now() - make_interval(mins => $1)`, [PENDING_MINUTES]);
|
|
97
106
|
await owner.query(
|
|
98
|
-
`insert into meta.sso_pending (state, app_id, provider_id, code_verifier, nonce, browser_hash, next)
|
|
99
|
-
values ($1, $2, $3, $4, $5, $6, $7)`,
|
|
100
|
-
[state, appId, p.id, verifier, nonce, sha256(browserKey), next ?? null],
|
|
107
|
+
`insert into meta.sso_pending (state, app_id, provider_id, code_verifier, nonce, browser_hash, next, link_username)
|
|
108
|
+
values ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
|
109
|
+
[state, appId, p.id, verifier, nonce, sha256(browserKey), next ?? null, link ?? null],
|
|
101
110
|
);
|
|
102
111
|
const url = new URL(doc.authorization_endpoint);
|
|
103
112
|
url.search = new URLSearchParams({
|
|
@@ -113,16 +122,29 @@ export async function startSignIn(p: Provider, appId: number, next: string | und
|
|
|
113
122
|
return { url: url.toString(), browserKey };
|
|
114
123
|
}
|
|
115
124
|
|
|
125
|
+
// (sprint 129) the builder finishes its own sign-ins (app_id null); the callback routes are the runtime's
|
|
126
|
+
type BuilderFinish = (req: any, reply: any, p: Provider, result: SsoResult, ip: string) => Promise<unknown>;
|
|
127
|
+
let builderFinish: BuilderFinish | null = null;
|
|
128
|
+
export const onBuilderSso = (f: BuilderFinish) => {
|
|
129
|
+
builderFinish = f;
|
|
130
|
+
};
|
|
131
|
+
export const builderSsoFinish = () => builderFinish;
|
|
132
|
+
|
|
116
133
|
/** Read a claim by name or dot path (e.g. realm_access.roles). */
|
|
117
134
|
function claim(payload: JWTPayload, path: string): unknown {
|
|
118
135
|
return path.split('.').reduce<any>((v, k) => (v && typeof v === 'object' ? v[k] : undefined), payload);
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
export interface SsoResult {
|
|
122
|
-
|
|
139
|
+
/** null: a sign-in to the builder (131); username is then the provider's, not resolved to an account */
|
|
140
|
+
appId: number | null;
|
|
123
141
|
next: string | null;
|
|
124
142
|
username: string;
|
|
125
143
|
groups: string[];
|
|
144
|
+
/** builder sign-ins: the identity, for devsso.ts */
|
|
145
|
+
identity?: { subject: string; name: string | null; email: string | null; mayLink: boolean };
|
|
146
|
+
/** builder: the signed-in developer who links this identity (Security page) */
|
|
147
|
+
link?: string | null;
|
|
126
148
|
}
|
|
127
149
|
|
|
128
150
|
/** Callback: validate state/browser, exchange the code, verify the ID token, resolve the account. */
|
|
@@ -132,7 +154,7 @@ export async function finishSignIn(p: Provider, params: URLSearchParams, browser
|
|
|
132
154
|
const code = params.get('code') ?? '';
|
|
133
155
|
const pending = await owner.one(
|
|
134
156
|
`delete from meta.sso_pending where state = $1 and provider_id = $2 and created_at > now() - make_interval(mins => $3)
|
|
135
|
-
returning app_id, code_verifier, nonce, browser_hash, next`,
|
|
157
|
+
returning app_id, code_verifier, nonce, browser_hash, next, link_username`,
|
|
136
158
|
[state, p.id, PENDING_MINUTES],
|
|
137
159
|
);
|
|
138
160
|
if (!pending) throw new SsoError('This sign-in link has expired or was already used. Please try again.');
|
|
@@ -181,6 +203,14 @@ export async function finishSignIn(p: Provider, params: URLSearchParams, browser
|
|
|
181
203
|
|
|
182
204
|
// an unverified e-mail address never links an existing account
|
|
183
205
|
const verified = p.username_claim !== 'email' || payload.email_verified === true;
|
|
206
|
+
if (pending.app_id === null) {
|
|
207
|
+
// the builder: devsso.ts finds the developer (sprint 129)
|
|
208
|
+
const email = payload.email_verified === false ? null : typeof payload.email === 'string' ? payload.email : null;
|
|
209
|
+
return {
|
|
210
|
+
appId: null, next: pending.next, username, groups, link: pending.link_username,
|
|
211
|
+
identity: { subject: payload.sub, name: typeof payload.name === 'string' ? payload.name : null, email, mayLink: verified },
|
|
212
|
+
};
|
|
213
|
+
}
|
|
184
214
|
const account = await resolveAccount(p, payload, username, { mayLink: verified });
|
|
185
215
|
return { appId: pending.app_id, next: pending.next, username: account, groups };
|
|
186
216
|
}
|