@wtfalch/auth 0.6.0 → 0.8.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/README.md +15 -2
- package/dist/auth.d.ts +83 -0
- package/dist/auth.js +147 -7
- package/dist/broker.d.ts +27 -0
- package/dist/broker.js +17 -0
- package/dist/cookies.d.ts +14 -0
- package/dist/cookies.js +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/namespace-server.js +7 -3
- package/dist/namespace-session.js +27 -2
- package/dist/namespaces.d.ts +76 -2
- package/dist/namespaces.js +58 -9
- package/dist/next.d.ts +49 -2
- package/dist/next.js +51 -0
- package/dist/oidc.d.ts +5 -1
- package/dist/oidc.js +12 -8
- package/dist/passkey-browser.d.ts +83 -0
- package/dist/passkey-browser.js +150 -0
- package/dist/qr.d.ts +23 -0
- package/dist/qr.js +75 -0
- package/package.json +6 -2
package/dist/qr.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ResolvedOptions } from './config.js';
|
|
2
|
+
import { type Oidc, type Tokens } from './oidc.js';
|
|
3
|
+
export interface DeviceAuthorization {
|
|
4
|
+
deviceCode: string;
|
|
5
|
+
userCode: string;
|
|
6
|
+
verificationUriComplete: string;
|
|
7
|
+
expiresIn: number;
|
|
8
|
+
interval: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function requestDeviceAuthorization(options: ResolvedOptions, scope: string): Promise<DeviceAuthorization>;
|
|
11
|
+
export type DevicePoll = {
|
|
12
|
+
status: 'pending';
|
|
13
|
+
} | {
|
|
14
|
+
status: 'slow_down';
|
|
15
|
+
} | {
|
|
16
|
+
status: 'expired';
|
|
17
|
+
} | {
|
|
18
|
+
status: 'denied';
|
|
19
|
+
} | {
|
|
20
|
+
status: 'signed_in';
|
|
21
|
+
tokens: Tokens;
|
|
22
|
+
};
|
|
23
|
+
export declare function pollDeviceToken(options: ResolvedOptions, oidc: Oidc, deviceCode: string): Promise<DevicePoll>;
|
package/dist/qr.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { AuthError } from './oidc.js';
|
|
2
|
+
/**
|
|
3
|
+
* The OAuth device authorization grant (RFC 8628), for signing in a new
|
|
4
|
+
* device from a QR code a phone that is already signed in approves. Raw
|
|
5
|
+
* `fetch` against the issuer's own endpoints -- the same convention
|
|
6
|
+
* `browser.ts` and `namespace-browser.ts` use for their token calls -- rather
|
|
7
|
+
* than `openid-client`'s device-grant helpers, which poll synchronously and
|
|
8
|
+
* do not fit a request served once per browser poll.
|
|
9
|
+
*/
|
|
10
|
+
const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
|
|
11
|
+
export async function requestDeviceAuthorization(options, scope) {
|
|
12
|
+
const response = await options.fetch(`${options.issuer}/oauth/v2/device_authorization`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
15
|
+
body: new URLSearchParams({ client_id: options.clientId, scope }),
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw new AuthError('request', `the issuer refused the device authorization request (${response.status})`);
|
|
19
|
+
}
|
|
20
|
+
const body = (await response.json().catch(() => null));
|
|
21
|
+
const deviceCode = str(body?.device_code);
|
|
22
|
+
const userCode = str(body?.user_code);
|
|
23
|
+
const verificationUriComplete = str(body?.verification_uri_complete);
|
|
24
|
+
const expiresIn = num(body?.expires_in);
|
|
25
|
+
if (!deviceCode || !userCode || !verificationUriComplete || expiresIn === null) {
|
|
26
|
+
throw new AuthError('request', 'the issuer returned an incomplete device authorization');
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
deviceCode,
|
|
30
|
+
userCode,
|
|
31
|
+
verificationUriComplete,
|
|
32
|
+
expiresIn,
|
|
33
|
+
interval: num(body?.interval) ?? 5,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export async function pollDeviceToken(options, oidc, deviceCode) {
|
|
37
|
+
const response = await options.fetch(`${options.issuer}/oauth/v2/token`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
40
|
+
body: new URLSearchParams({
|
|
41
|
+
grant_type: DEVICE_GRANT_TYPE,
|
|
42
|
+
device_code: deviceCode,
|
|
43
|
+
client_id: options.clientId,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
const body = (await response.json().catch(() => null));
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const error = str(body?.error);
|
|
49
|
+
if (error === 'authorization_pending')
|
|
50
|
+
return { status: 'pending' };
|
|
51
|
+
if (error === 'slow_down')
|
|
52
|
+
return { status: 'slow_down' };
|
|
53
|
+
if (error === 'expired_token')
|
|
54
|
+
return { status: 'expired' };
|
|
55
|
+
if (error === 'access_denied')
|
|
56
|
+
return { status: 'denied' };
|
|
57
|
+
throw new AuthError('exchange', str(body?.error_description) ?? error ?? 'device token request failed');
|
|
58
|
+
}
|
|
59
|
+
const idToken = str(body?.id_token);
|
|
60
|
+
if (!idToken)
|
|
61
|
+
throw new AuthError('exchange', 'the issuer returned no id token');
|
|
62
|
+
// Signature, issuer, audience, expiry and the organisation claim -- exactly
|
|
63
|
+
// the code-exchange path's checks, minus the nonce a device grant has none of.
|
|
64
|
+
const claims = await oidc.verify(idToken);
|
|
65
|
+
return {
|
|
66
|
+
status: 'signed_in',
|
|
67
|
+
tokens: { idToken, refreshToken: str(body?.refresh_token) ?? undefined, claims },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function str(value) {
|
|
71
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
72
|
+
}
|
|
73
|
+
function num(value) {
|
|
74
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
75
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtfalch/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Sign in against auth.wtfalch.dev: a server session for a Next.js app, and a browser client with silent single sign-on across subdomains.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "https://github.com/wtfalch/auth",
|
|
7
|
+
"url": "git+https://github.com/wtfalch/auth.git",
|
|
8
8
|
"directory": "packages/auth"
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"types": "./dist/browser.d.ts",
|
|
23
23
|
"default": "./dist/browser.js"
|
|
24
24
|
},
|
|
25
|
+
"./passkey": {
|
|
26
|
+
"types": "./dist/passkey-browser.d.ts",
|
|
27
|
+
"default": "./dist/passkey-browser.js"
|
|
28
|
+
},
|
|
25
29
|
"./namespaces": {
|
|
26
30
|
"types": "./dist/namespaces.d.ts",
|
|
27
31
|
"default": "./dist/namespaces.js"
|