@atlasauth/js 0.23.1 → 0.25.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/dist/app-launcher.d.ts +11 -0
  3. package/dist/app-launcher.js +12 -0
  4. package/dist/app-launcher.js.map +1 -0
  5. package/dist/attempt.d.ts +52 -1
  6. package/dist/attempt.js +2 -0
  7. package/dist/attempt.js.map +1 -1
  8. package/dist/attestation.d.ts +52 -0
  9. package/dist/attestation.js +48 -0
  10. package/dist/attestation.js.map +1 -0
  11. package/dist/authorized-apps.d.ts +42 -0
  12. package/dist/authorized-apps.js +41 -0
  13. package/dist/authorized-apps.js.map +1 -0
  14. package/dist/compat.d.ts +1 -0
  15. package/dist/compat.js +5 -1
  16. package/dist/compat.js.map +1 -1
  17. package/dist/connect.d.ts +6 -0
  18. package/dist/connect.js +22 -0
  19. package/dist/connect.js.map +1 -1
  20. package/dist/connections.d.ts +161 -0
  21. package/dist/connections.js +117 -0
  22. package/dist/connections.js.map +1 -0
  23. package/dist/credentials.d.ts +47 -0
  24. package/dist/credentials.js +95 -0
  25. package/dist/credentials.js.map +1 -0
  26. package/dist/custom-challenge.d.ts +13 -0
  27. package/dist/custom-challenge.js +27 -0
  28. package/dist/custom-challenge.js.map +1 -0
  29. package/dist/delegated-ui.d.ts +93 -0
  30. package/dist/delegated-ui.js +62 -0
  31. package/dist/delegated-ui.js.map +1 -0
  32. package/dist/dev-browser.d.ts +9 -0
  33. package/dist/dev-browser.js +21 -1
  34. package/dist/dev-browser.js.map +1 -1
  35. package/dist/device-key.d.ts +65 -0
  36. package/dist/device-key.js +64 -0
  37. package/dist/device-key.js.map +1 -0
  38. package/dist/email-addresses.d.ts +24 -0
  39. package/dist/email-addresses.js +36 -0
  40. package/dist/email-addresses.js.map +1 -0
  41. package/dist/environment.d.ts +8 -0
  42. package/dist/environment.js +6 -0
  43. package/dist/environment.js.map +1 -1
  44. package/dist/external-passkeys.d.ts +34 -0
  45. package/dist/external-passkeys.js +31 -0
  46. package/dist/external-passkeys.js.map +1 -0
  47. package/dist/fapi.d.ts +36 -0
  48. package/dist/fapi.js +75 -4
  49. package/dist/fapi.js.map +1 -1
  50. package/dist/identity-verification.d.ts +40 -0
  51. package/dist/identity-verification.js +40 -0
  52. package/dist/identity-verification.js.map +1 -0
  53. package/dist/iga.d.ts +118 -0
  54. package/dist/iga.js +60 -0
  55. package/dist/iga.js.map +1 -0
  56. package/dist/index.d.ts +21 -1
  57. package/dist/index.js +31 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/journey-redirect.d.ts +8 -0
  60. package/dist/journey-redirect.js +32 -0
  61. package/dist/journey-redirect.js.map +1 -0
  62. package/dist/marketing.d.ts +81 -0
  63. package/dist/marketing.js +162 -0
  64. package/dist/marketing.js.map +1 -0
  65. package/dist/mfa-enrollment.d.ts +2 -0
  66. package/dist/mfa-enrollment.js +1 -0
  67. package/dist/mfa-enrollment.js.map +1 -1
  68. package/dist/mfa-factors.d.ts +109 -0
  69. package/dist/mfa-factors.js +98 -0
  70. package/dist/mfa-factors.js.map +1 -0
  71. package/dist/migration-browser-sessions.d.ts +118 -0
  72. package/dist/migration-browser-sessions.js +404 -0
  73. package/dist/migration-browser-sessions.js.map +1 -0
  74. package/dist/organization-admin.d.ts +192 -0
  75. package/dist/organization-admin.js +53 -0
  76. package/dist/organization-admin.js.map +1 -0
  77. package/dist/passkey.d.ts +71 -2
  78. package/dist/passkey.js +97 -0
  79. package/dist/passkey.js.map +1 -1
  80. package/dist/password-reset.d.ts +16 -1
  81. package/dist/password-reset.js +19 -4
  82. package/dist/password-reset.js.map +1 -1
  83. package/dist/pricing.d.ts +80 -0
  84. package/dist/pricing.js +71 -0
  85. package/dist/pricing.js.map +1 -0
  86. package/dist/profile.d.ts +73 -1
  87. package/dist/profile.js +23 -0
  88. package/dist/profile.js.map +1 -1
  89. package/dist/redirect.js +1 -1
  90. package/dist/redirect.js.map +1 -1
  91. package/dist/srp.d.ts +55 -0
  92. package/dist/srp.js +120 -0
  93. package/dist/srp.js.map +1 -0
  94. package/dist/telegram.d.ts +23 -0
  95. package/dist/telegram.js +8 -0
  96. package/dist/telegram.js.map +1 -1
  97. package/dist/uma.d.ts +86 -0
  98. package/dist/uma.js +73 -0
  99. package/dist/uma.js.map +1 -0
  100. package/dist/version.d.ts +1 -1
  101. package/dist/version.js +1 -1
  102. package/package.json +2 -2
@@ -0,0 +1,161 @@
1
+ import type { FapiClient } from './fapi';
2
+ /**
3
+ * Connected accounts: the connections an app defined for the signed-in user or
4
+ * their active organization (an API key or client-credentials the org entered, or
5
+ * an OAuth account a member shared), with live status. Authorizing an OAuth
6
+ * connection uses `connectExternalAccount` / `reauthorizeExternalAccount`.
7
+ */
8
+ export type ConnectionStatus = 'connected' | 'not_connected' | 'needs_reauth' | 'revoked' | 'error';
9
+ export interface AppConnection {
10
+ object: 'connection';
11
+ id: string;
12
+ owner: {
13
+ type: 'organization' | 'user';
14
+ id: string;
15
+ };
16
+ kind: 'oauth' | 'api_key' | 'client_credentials' | 'bearer' | 'basic' | 'custom_header' | 'hmac_secret' | 'jwt_bearer' | 'oauth1' | 'github_app_installation' | 'admin_consent';
17
+ provider: string;
18
+ name: string;
19
+ allowed_hosts: string[];
20
+ status: ConnectionStatus;
21
+ /** Always `data`: a connection is never a sign-in method. */
22
+ purpose?: 'data';
23
+ environment?: 'production' | 'sandbox';
24
+ sandbox?: boolean;
25
+ /** The provider-side account the connection covers (a company, a workspace, a user id). */
26
+ external_id?: string | null;
27
+ host_params?: Record<string, string>;
28
+ scopes?: string[];
29
+ expires_at?: number | null;
30
+ /** Non-secret provider handles captured with the token (realmId, instance_url, team_id...). */
31
+ metadata?: Record<string, string>;
32
+ /** The provider's own error behind the status (`invalid_grant`, ...), with the HTTP status it answered. */
33
+ last_error?: {
34
+ code: string;
35
+ message: string;
36
+ httpStatus?: number | null;
37
+ at: number;
38
+ } | null;
39
+ /** Consecutive failed refreshes; 0 when healthy. */
40
+ refresh_failures?: number;
41
+ /** @deprecated Always null: a connection owns its tokens. */
42
+ external_account_id: string | null;
43
+ has_secret: boolean;
44
+ /** When the entered credential expires (epoch ms). The credential itself is never readable from the browser. */
45
+ secret_expires_at?: number | null;
46
+ }
47
+ /** What the provider said when a connection was disconnected. The stored token is cleared in every case. */
48
+ export interface ConnectionRevokeResult {
49
+ /** `true` the provider confirmed; `'unsupported'` it has no revoke endpoint; `false` it refused or was unreachable (see `provider_error`). */
50
+ provider_revoked: boolean | 'unsupported' | 'not_connected';
51
+ provider_status: number | null;
52
+ provider_error: {
53
+ code: string;
54
+ description: string | null;
55
+ } | null;
56
+ local_cleared: true;
57
+ }
58
+ /** What to send when starting the provider authorization of a connection. */
59
+ export interface ConnectConnectionInput {
60
+ /** Where the owner lands afterwards; the URL carries `__atlas_status=connected` and `__atlas_connection=<id>`. */
61
+ redirectUrl: string;
62
+ /** Values for the provider's host templates, e.g. `{ subdomain: 'acme' }` or `{ tenant: 'contoso.onmicrosoft.com' }`. */
63
+ hostParams?: Record<string, string>;
64
+ environment?: 'production' | 'sandbox';
65
+ additionalScopes?: string[];
66
+ loginHint?: string;
67
+ display?: 'redirect' | 'popup';
68
+ /** A different provider account than the connection holds creates a sibling instead of failing with 409. */
69
+ createIfNew?: boolean;
70
+ }
71
+ export interface ConnectionAuthorization {
72
+ object: 'connection_authorization';
73
+ connection_id: string;
74
+ authorization_url: string;
75
+ attempt_id: string;
76
+ }
77
+ /** An organization's own OAuth client for a provider (the secret is write-only). */
78
+ export interface ConnectionOAuthClient {
79
+ object: 'connection_oauth_client';
80
+ id: string;
81
+ provider: string;
82
+ environment: 'production' | 'sandbox';
83
+ client_id: string;
84
+ has_secret: true;
85
+ scopes: string[];
86
+ host_params: Record<string, string>;
87
+ enabled: boolean;
88
+ callback_url: string;
89
+ }
90
+ export interface SetConnectionOAuthClientInput {
91
+ provider: string;
92
+ environment?: 'production' | 'sandbox';
93
+ clientId: string;
94
+ /** Required the first time; omit to keep the stored secret. */
95
+ clientSecret?: string;
96
+ scopes?: string[];
97
+ hostParams?: Record<string, string>;
98
+ enabled?: boolean;
99
+ }
100
+ /**
101
+ * Credential to enter for a non-OAuth connection. Write-only: it is sealed server-side and no browser call can read it
102
+ * back. Which fields apply depends on the connection's kind.
103
+ */
104
+ export interface ConnectionCredentialsInput {
105
+ /** api_key */
106
+ apiKey?: string;
107
+ /** bearer */
108
+ token?: string;
109
+ /** basic */
110
+ username?: string;
111
+ password?: string;
112
+ /** custom_header */
113
+ value?: string;
114
+ /** hmac_secret */
115
+ secret?: string;
116
+ /** client_credentials */
117
+ clientId?: string;
118
+ clientSecret?: string;
119
+ tokenUrl?: string;
120
+ /** jwt_bearer / github_app_installation: a PEM private key, or (jwt_bearer) a Google service-account JSON key. */
121
+ privateKey?: string;
122
+ serviceAccountJson?: string;
123
+ /** oauth1 */
124
+ consumerKey?: string;
125
+ consumerSecret?: string;
126
+ tokenSecret?: string;
127
+ /** Epoch milliseconds or an ISO date; reminders go out 30, 7 and 1 days before. */
128
+ expiresAt?: number | string;
129
+ /** Non-secret settings of the kind (placement, header name, algorithm...). */
130
+ config?: Record<string, unknown>;
131
+ }
132
+ export declare class ConnectionRequestError extends Error {
133
+ readonly status: number;
134
+ readonly code: string;
135
+ constructor(status: number, code: string, message: string);
136
+ }
137
+ /** The signed-in user's own connections. */
138
+ export declare function listMyConnections(client: FapiClient): Promise<AppConnection[]>;
139
+ /** The active organization's connections. */
140
+ export declare function listOrganizationConnections(client: FapiClient, organizationId: string): Promise<AppConnection[]>;
141
+ /** Enter the organization's credential for a connection (needs the org profile-manage permission). */
142
+ export declare function setOrganizationConnectionCredentials(client: FapiClient, organizationId: string, connectionId: string, input: ConnectionCredentialsInput): Promise<AppConnection>;
143
+ /** Share the signed-in admin's own linked provider account with the organization. */
144
+ export declare function shareOrganizationConnection(client: FapiClient, organizationId: string, connectionId: string): Promise<AppConnection>;
145
+ /**
146
+ * Connect (or reconnect) one of the signed-in user's own connections. Navigate to `authorization_url`. This stores the
147
+ * tokens on the connection: it adds no sign-in method and needs no step-up.
148
+ */
149
+ export declare function connectMyConnection(client: FapiClient, connectionId: string, input: ConnectConnectionInput): Promise<ConnectionAuthorization>;
150
+ /** Disconnect one of the signed-in user's own connections. The answer says whether the provider confirmed the revoke. */
151
+ export declare function revokeMyConnection(client: FapiClient, connectionId: string): Promise<AppConnection & ConnectionRevokeResult>;
152
+ /** Connect (or reconnect) an organization's connection (needs the org profile-manage permission, not a step-up). */
153
+ export declare function connectOrganizationConnection(client: FapiClient, organizationId: string, connectionId: string, input: ConnectConnectionInput): Promise<ConnectionAuthorization>;
154
+ /** Disconnect an organization's connection. */
155
+ export declare function revokeOrganizationConnection(client: FapiClient, organizationId: string, connectionId: string): Promise<AppConnection & ConnectionRevokeResult>;
156
+ /** The organization's own OAuth clients. */
157
+ export declare function listOrganizationOAuthClients(client: FapiClient, organizationId: string): Promise<ConnectionOAuthClient[]>;
158
+ /** Set the organization's own OAuth client for a provider; it overrides the instance client for its connections. */
159
+ export declare function setOrganizationOAuthClient(client: FapiClient, organizationId: string, input: SetConnectionOAuthClientInput): Promise<ConnectionOAuthClient>;
160
+ /** Delete the organization's OAuth client (refused while its connections hold tokens it issued). */
161
+ export declare function deleteOrganizationOAuthClient(client: FapiClient, organizationId: string, clientId: string): Promise<void>;
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConnectionRequestError = void 0;
4
+ exports.listMyConnections = listMyConnections;
5
+ exports.listOrganizationConnections = listOrganizationConnections;
6
+ exports.setOrganizationConnectionCredentials = setOrganizationConnectionCredentials;
7
+ exports.shareOrganizationConnection = shareOrganizationConnection;
8
+ exports.connectMyConnection = connectMyConnection;
9
+ exports.revokeMyConnection = revokeMyConnection;
10
+ exports.connectOrganizationConnection = connectOrganizationConnection;
11
+ exports.revokeOrganizationConnection = revokeOrganizationConnection;
12
+ exports.listOrganizationOAuthClients = listOrganizationOAuthClients;
13
+ exports.setOrganizationOAuthClient = setOrganizationOAuthClient;
14
+ exports.deleteOrganizationOAuthClient = deleteOrganizationOAuthClient;
15
+ class ConnectionRequestError extends Error {
16
+ status;
17
+ code;
18
+ constructor(status, code, message) {
19
+ super(message);
20
+ this.status = status;
21
+ this.code = code;
22
+ this.name = 'ConnectionRequestError';
23
+ }
24
+ }
25
+ exports.ConnectionRequestError = ConnectionRequestError;
26
+ async function call(client, path, init) {
27
+ const res = await client.request(path, init);
28
+ if (!res.ok || !res.data) {
29
+ const first = res.errors[0];
30
+ throw new ConnectionRequestError(res.status, first?.code ?? 'UNKNOWN', first?.message ?? 'The request failed.');
31
+ }
32
+ return res.data;
33
+ }
34
+ const orgBase = (organizationId) => `/v1/client/organizations/${encodeURIComponent(organizationId)}/connections`;
35
+ /** The signed-in user's own connections. */
36
+ async function listMyConnections(client) {
37
+ return (await call(client, '/v1/client/me/connections', { method: 'GET' })).data ?? [];
38
+ }
39
+ /** The active organization's connections. */
40
+ async function listOrganizationConnections(client, organizationId) {
41
+ return (await call(client, orgBase(organizationId), { method: 'GET' })).data ?? [];
42
+ }
43
+ /** Enter the organization's credential for a connection (needs the org profile-manage permission). */
44
+ function setOrganizationConnectionCredentials(client, organizationId, connectionId, input) {
45
+ const map = [
46
+ ['apiKey', 'api_key'], ['token', 'token'], ['username', 'username'], ['password', 'password'], ['value', 'value'], ['secret', 'secret'],
47
+ ['clientId', 'client_id'], ['clientSecret', 'client_secret'], ['tokenUrl', 'token_url'], ['privateKey', 'private_key'],
48
+ ['serviceAccountJson', 'service_account_json'], ['consumerKey', 'consumer_key'], ['consumerSecret', 'consumer_secret'],
49
+ ['tokenSecret', 'token_secret'], ['expiresAt', 'expires_at'], ['config', 'config'],
50
+ ];
51
+ const body = {};
52
+ for (const [from, to] of map)
53
+ if (input[from] !== undefined)
54
+ body[to] = input[from];
55
+ return call(client, `${orgBase(organizationId)}/${encodeURIComponent(connectionId)}/credentials`, { method: 'PUT', body: JSON.stringify(body) });
56
+ }
57
+ /** Share the signed-in admin's own linked provider account with the organization. */
58
+ function shareOrganizationConnection(client, organizationId, connectionId) {
59
+ return call(client, `${orgBase(organizationId)}/${encodeURIComponent(connectionId)}/share`, { method: 'POST', body: '{}' });
60
+ }
61
+ const connectBody = (i) => ({
62
+ redirect_url: i.redirectUrl,
63
+ ...(i.hostParams ? { host_params: i.hostParams } : {}),
64
+ ...(i.environment ? { environment: i.environment } : {}),
65
+ ...(i.additionalScopes ? { additional_scopes: i.additionalScopes } : {}),
66
+ ...(i.loginHint ? { login_hint: i.loginHint } : {}),
67
+ ...(i.display ? { display: i.display } : {}),
68
+ ...(i.createIfNew ? { create_if_new: true } : {}),
69
+ });
70
+ /**
71
+ * Connect (or reconnect) one of the signed-in user's own connections. Navigate to `authorization_url`. This stores the
72
+ * tokens on the connection: it adds no sign-in method and needs no step-up.
73
+ */
74
+ function connectMyConnection(client, connectionId, input) {
75
+ return call(client, `/v1/client/me/connections/${encodeURIComponent(connectionId)}/connect`, { method: 'POST', body: JSON.stringify(connectBody(input)) });
76
+ }
77
+ /** Disconnect one of the signed-in user's own connections. The answer says whether the provider confirmed the revoke. */
78
+ function revokeMyConnection(client, connectionId) {
79
+ return call(client, `/v1/client/me/connections/${encodeURIComponent(connectionId)}/revoke`, { method: 'POST', body: '{}' });
80
+ }
81
+ /** Connect (or reconnect) an organization's connection (needs the org profile-manage permission, not a step-up). */
82
+ function connectOrganizationConnection(client, organizationId, connectionId, input) {
83
+ return call(client, `${orgBase(organizationId)}/${encodeURIComponent(connectionId)}/connect`, { method: 'POST', body: JSON.stringify(connectBody(input)) });
84
+ }
85
+ /** Disconnect an organization's connection. */
86
+ function revokeOrganizationConnection(client, organizationId, connectionId) {
87
+ return call(client, `${orgBase(organizationId)}/${encodeURIComponent(connectionId)}/revoke`, { method: 'POST', body: '{}' });
88
+ }
89
+ const clientsBase = (organizationId) => `/v1/client/organizations/${encodeURIComponent(organizationId)}/connection_oauth_clients`;
90
+ /** The organization's own OAuth clients. */
91
+ async function listOrganizationOAuthClients(client, organizationId) {
92
+ return (await call(client, clientsBase(organizationId), { method: 'GET' })).data ?? [];
93
+ }
94
+ /** Set the organization's own OAuth client for a provider; it overrides the instance client for its connections. */
95
+ function setOrganizationOAuthClient(client, organizationId, input) {
96
+ return call(client, clientsBase(organizationId), {
97
+ method: 'PUT',
98
+ body: JSON.stringify({
99
+ provider: input.provider,
100
+ ...(input.environment ? { environment: input.environment } : {}),
101
+ client_id: input.clientId,
102
+ ...(input.clientSecret ? { client_secret: input.clientSecret } : {}),
103
+ ...(input.scopes ? { scopes: input.scopes } : {}),
104
+ ...(input.hostParams ? { host_params: input.hostParams } : {}),
105
+ ...(input.enabled !== undefined ? { enabled: input.enabled } : {}),
106
+ }),
107
+ });
108
+ }
109
+ /** Delete the organization's OAuth client (refused while its connections hold tokens it issued). */
110
+ async function deleteOrganizationOAuthClient(client, organizationId, clientId) {
111
+ const res = await client.request(`${clientsBase(organizationId)}/${encodeURIComponent(clientId)}`, { method: 'DELETE' });
112
+ if (!res.ok) {
113
+ const first = res.errors[0];
114
+ throw new ConnectionRequestError(res.status, first?.code ?? 'UNKNOWN', first?.message ?? 'The request failed.');
115
+ }
116
+ }
117
+ //# sourceMappingURL=connections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../src/connections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Wallet-side helpers for Atlas verifiable credentials (OpenID4VCI / OpenID4VP,
3
+ * SD-JWT VC): redeem a credential offer, hold the credential with its device
4
+ * key, and build a selective presentation. For web wallets, tests and demos.
5
+ * Uses WebCrypto (ES256) only. mdoc credentials are received as-is
6
+ * (`format: 'mso_mdoc'`); building an mdoc DeviceResponse is a native-wallet job.
7
+ */
8
+ export interface CredentialOffer {
9
+ credential_issuer: string;
10
+ credential_configuration_ids: string[];
11
+ grants?: Record<string, {
12
+ 'pre-authorized_code'?: string;
13
+ tx_code?: {
14
+ length?: number;
15
+ };
16
+ }>;
17
+ }
18
+ /** The offer inside an `openid-credential-offer://` link (or an https link carrying `credential_offer`). */
19
+ export declare function parseCredentialOffer(link: string): CredentialOffer;
20
+ export interface HolderKey {
21
+ privateJwk: JsonWebKey;
22
+ publicJwk: JsonWebKey;
23
+ }
24
+ /** A fresh P-256 device key. */
25
+ export declare function generateHolderKey(): Promise<HolderKey>;
26
+ export interface ReceivedCredential extends HolderKey {
27
+ credential: string;
28
+ format: 'dc+sd-jwt' | 'mso_mdoc';
29
+ }
30
+ /** Redeem an offer (pre-authorized code flow) and receive the credential bound to a new device key. */
31
+ export declare function receiveCredential(offer: CredentialOffer, opts?: {
32
+ txCode?: string;
33
+ fetch?: typeof fetch;
34
+ key?: HolderKey;
35
+ configurationId?: string;
36
+ }): Promise<ReceivedCredential>;
37
+ /**
38
+ * Build an SD-JWT VC presentation revealing only the claims in `reveal`, with a
39
+ * key-binding JWT over `aud` and `nonce` (from the verifier's request object).
40
+ */
41
+ export declare function presentSdJwt(input: {
42
+ credential: string;
43
+ reveal: string[];
44
+ aud: string;
45
+ nonce: string;
46
+ privateJwk: JsonWebKey;
47
+ }): Promise<string>;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Wallet-side helpers for Atlas verifiable credentials (OpenID4VCI / OpenID4VP,
4
+ * SD-JWT VC): redeem a credential offer, hold the credential with its device
5
+ * key, and build a selective presentation. For web wallets, tests and demos.
6
+ * Uses WebCrypto (ES256) only. mdoc credentials are received as-is
7
+ * (`format: 'mso_mdoc'`); building an mdoc DeviceResponse is a native-wallet job.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.parseCredentialOffer = parseCredentialOffer;
11
+ exports.generateHolderKey = generateHolderKey;
12
+ exports.receiveCredential = receiveCredential;
13
+ exports.presentSdJwt = presentSdJwt;
14
+ const PRE_AUTH = 'urn:ietf:params:oauth:grant-type:pre-authorized_code';
15
+ /** The offer inside an `openid-credential-offer://` link (or an https link carrying `credential_offer`). */
16
+ function parseCredentialOffer(link) {
17
+ const raw = new URL(link).searchParams.get('credential_offer');
18
+ if (!raw)
19
+ throw new Error('The link does not carry a credential_offer.');
20
+ const offer = JSON.parse(raw);
21
+ if (typeof offer.credential_issuer !== 'string' || !Array.isArray(offer.credential_configuration_ids)) {
22
+ throw new Error('Malformed credential offer.');
23
+ }
24
+ return offer;
25
+ }
26
+ const enc = new TextEncoder();
27
+ const b64u = (b) => {
28
+ const bytes = b instanceof Uint8Array ? b : new Uint8Array(b);
29
+ let s = '';
30
+ for (const x of bytes)
31
+ s += String.fromCharCode(x);
32
+ return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
33
+ };
34
+ const b64uJson = (v) => b64u(enc.encode(JSON.stringify(v)));
35
+ /** A fresh P-256 device key. */
36
+ async function generateHolderKey() {
37
+ const pair = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']);
38
+ const publicJwk = await crypto.subtle.exportKey('jwk', pair.publicKey);
39
+ return { privateJwk: await crypto.subtle.exportKey('jwk', pair.privateKey), publicJwk: { ...publicJwk, alg: 'ES256' } };
40
+ }
41
+ async function signJwt(header, payload, privateJwk) {
42
+ const key = await crypto.subtle.importKey('jwk', privateJwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
43
+ const input = `${b64uJson({ alg: 'ES256', ...header })}.${b64uJson(payload)}`;
44
+ const sig = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, key, enc.encode(input));
45
+ return `${input}.${b64u(sig)}`;
46
+ }
47
+ async function json(res) {
48
+ const body = (await res.json().catch(() => ({})));
49
+ if (!res.ok)
50
+ throw new Error(body.error_description ?? body.error ?? `Request failed (${res.status}).`);
51
+ return body;
52
+ }
53
+ /** Redeem an offer (pre-authorized code flow) and receive the credential bound to a new device key. */
54
+ async function receiveCredential(offer, opts = {}) {
55
+ const f = opts.fetch ?? ((...a) => fetch(...a));
56
+ const code = offer.grants?.[PRE_AUTH]?.['pre-authorized_code'];
57
+ if (!code)
58
+ throw new Error('The offer has no pre-authorized code.');
59
+ const issuer = offer.credential_issuer.replace(/\/$/, '');
60
+ const meta = await json(await f(`${issuer}/.well-known/openid-credential-issuer`));
61
+ const asBase = (meta.authorization_servers?.[0] ?? issuer).replace(/\/$/, '');
62
+ const as = await json(await f(`${asBase}/.well-known/oauth-authorization-server`));
63
+ const configurationId = opts.configurationId ?? offer.credential_configuration_ids[0];
64
+ const token = await json(await f(as.token_endpoint, {
65
+ method: 'POST',
66
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
67
+ body: new URLSearchParams({ grant_type: PRE_AUTH, 'pre-authorized_code': code, ...(opts.txCode ? { tx_code: opts.txCode } : {}) }).toString(),
68
+ }));
69
+ const key = opts.key ?? (await generateHolderKey());
70
+ const proof = await signJwt({ typ: 'openid4vci-proof+jwt', jwk: { kty: key.publicJwk.kty, crv: key.publicJwk.crv, x: key.publicJwk.x, y: key.publicJwk.y, alg: 'ES256' } }, { aud: issuer, iat: Math.floor(Date.now() / 1000), nonce: token.c_nonce }, key.privateJwk);
71
+ const issued = await json(await f(meta.credential_endpoint, {
72
+ method: 'POST',
73
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${token.access_token}` },
74
+ body: JSON.stringify({ credential_configuration_id: configurationId, proof: { proof_type: 'jwt', jwt: proof } }),
75
+ }));
76
+ return { ...key, credential: issued.credential, format: issued.format ?? meta.credential_configurations_supported?.[configurationId]?.format ?? 'dc+sd-jwt' };
77
+ }
78
+ /**
79
+ * Build an SD-JWT VC presentation revealing only the claims in `reveal`, with a
80
+ * key-binding JWT over `aud` and `nonce` (from the verifier's request object).
81
+ */
82
+ async function presentSdJwt(input) {
83
+ const [jwt, ...rest] = input.credential.split('~').filter(Boolean);
84
+ if (!jwt)
85
+ throw new Error('Not an SD-JWT credential.');
86
+ const shown = rest.filter((d) => {
87
+ const name = JSON.parse(atob(d.replace(/-/g, '+').replace(/_/g, '/')));
88
+ return input.reveal.includes(String(name[1]));
89
+ });
90
+ const base = `${[jwt, ...shown].join('~')}~`;
91
+ const digest = await crypto.subtle.digest('SHA-256', enc.encode(base));
92
+ const kb = await signJwt({ typ: 'kb+jwt' }, { aud: input.aud, nonce: input.nonce, iat: Math.floor(Date.now() / 1000), sd_hash: b64u(digest) }, input.privateJwk);
93
+ return `${base}${kb}`;
94
+ }
95
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { FapiClient } from './fapi';
2
+ /**
3
+ * The application's own second factor (a question, a code sent elsewhere, an
4
+ * identity check): ask Atlas to run the application's challenge for this
5
+ * sign-in attempt, show the prompt, then submit the answer as the attempt's
6
+ * `code` with `strategy: 'custom_challenge'` (e.g. `submit({ code, strategy:
7
+ * 'custom_challenge' })`). Offered when `account.customSecondFactor` is true.
8
+ */
9
+ export declare function prepareCustomChallenge(client: FapiClient, attemptId: string): Promise<{
10
+ prompt: string;
11
+ hint: string | null;
12
+ expiresAt: number;
13
+ }>;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepareCustomChallenge = prepareCustomChallenge;
4
+ /**
5
+ * The application's own second factor (a question, a code sent elsewhere, an
6
+ * identity check): ask Atlas to run the application's challenge for this
7
+ * sign-in attempt, show the prompt, then submit the answer as the attempt's
8
+ * `code` with `strategy: 'custom_challenge'` (e.g. `submit({ code, strategy:
9
+ * 'custom_challenge' })`). Offered when `account.customSecondFactor` is true.
10
+ */
11
+ async function prepareCustomChallenge(client, attemptId) {
12
+ const response = await client.post(`/v1/client/sign_ins/${encodeURIComponent(attemptId)}/prepare_second_factor`, { strategy: 'custom_challenge' });
13
+ if (!response.ok) {
14
+ const first = response.errors[0];
15
+ const error = new Error(first?.message ?? 'Could not start the challenge.');
16
+ if (first?.code)
17
+ error.code = first.code;
18
+ throw error;
19
+ }
20
+ const d = response.data;
21
+ return {
22
+ prompt: typeof d?.prompt === 'string' ? d.prompt : '',
23
+ hint: typeof d?.hint === 'string' ? d.hint : null,
24
+ expiresAt: typeof d?.expires_at === 'number' ? d.expires_at : 0,
25
+ };
26
+ }
27
+ //# sourceMappingURL=custom-challenge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-challenge.js","sourceRoot":"","sources":["../src/custom-challenge.ts"],"names":[],"mappings":";;AASA,wDAoBC;AA3BD;;;;;;GAMG;AACI,KAAK,UAAU,sBAAsB,CAC1C,MAAkB,EAClB,SAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAChC,uBAAuB,kBAAkB,CAAC,SAAS,CAAC,wBAAwB,EAC5E,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CACjC,CAAC;IACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,gCAAgC,CAA8B,CAAC;QACzG,IAAI,KAAK,EAAE,IAAI;YAAE,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACzC,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC;IACxB,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;QACrD,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QACjD,SAAS,EAAE,OAAO,CAAC,EAAE,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;KAChE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Bring-your-own login and consent pages. Atlas sends the browser to your page
3
+ * with `?login_challenge=` or `?consent_challenge=`. Reading, accepting and
4
+ * rejecting the challenge needs a secret key, so your page talks to YOUR
5
+ * backend (see `delegatedUiProxy` in @atlasauth/backend), and then follows the
6
+ * `redirect_to` Atlas returned.
7
+ */
8
+ export type DelegatedChallenge = {
9
+ type: 'login' | 'consent';
10
+ challenge: string;
11
+ };
12
+ /** The challenge on the current page URL, or null when the page was not reached from Atlas. */
13
+ export declare function readDelegatedChallenge(href: string | URL | {
14
+ href: string;
15
+ }): DelegatedChallenge | null;
16
+ export interface DelegatedLoginRequest {
17
+ client: {
18
+ id: string;
19
+ client_id: string;
20
+ name: string;
21
+ };
22
+ requested_scope: string[];
23
+ audience: string | null;
24
+ page: 'sign-in' | 'sign-up';
25
+ session_subject: string | null;
26
+ ui_locales: string | null;
27
+ login_hint: string | null;
28
+ expires_at: number;
29
+ }
30
+ export interface DelegatedConsentRequest {
31
+ subject: string;
32
+ client: {
33
+ id: string;
34
+ client_id: string;
35
+ name: string;
36
+ first_party?: boolean;
37
+ };
38
+ requested_scope: string[];
39
+ audience: string | null;
40
+ authorization_details: unknown[];
41
+ organization_id: string | null;
42
+ redirect_uri: string;
43
+ expires_at: number;
44
+ }
45
+ export interface DelegatedUiClientOptions {
46
+ /** Base URL of your backend routes that proxy the Atlas calls, e.g. `/atlas/oauth`. */
47
+ baseUrl: string;
48
+ fetch?: typeof fetch;
49
+ headers?: Record<string, string>;
50
+ }
51
+ /**
52
+ * Calls your backend's proxy routes (`GET {baseUrl}/login`, `POST {baseUrl}/login/accept`, …),
53
+ * each taking the challenge as a query parameter. Errors throw with the server's message.
54
+ */
55
+ export declare function createDelegatedUiClient(opts: DelegatedUiClientOptions): {
56
+ getLoginRequest: (challenge: string) => Promise<DelegatedLoginRequest>;
57
+ /** `subject` is the Atlas user id your backend authenticated the visitor as. */
58
+ acceptLogin: (challenge: string, body: {
59
+ subject: string;
60
+ remember?: boolean;
61
+ amr?: string[];
62
+ acr?: string;
63
+ }) => Promise<{
64
+ redirect_to: string;
65
+ }>;
66
+ rejectLogin: (challenge: string, body?: {
67
+ error?: string;
68
+ error_description?: string;
69
+ }) => Promise<{
70
+ redirect_to: string;
71
+ }>;
72
+ getConsentRequest: (challenge: string) => Promise<DelegatedConsentRequest>;
73
+ acceptConsent: (challenge: string, body?: {
74
+ grant_scope?: string[];
75
+ remember?: boolean;
76
+ }) => Promise<{
77
+ redirect_to: string;
78
+ }>;
79
+ rejectConsent: (challenge: string, body?: {
80
+ error?: string;
81
+ error_description?: string;
82
+ }) => Promise<{
83
+ redirect_to: string;
84
+ }>;
85
+ };
86
+ /**
87
+ * Send the browser on to the `redirect_to` Atlas returned. Refuses anything that
88
+ * is not https (or an allowed origin), so a tampered response cannot redirect elsewhere.
89
+ */
90
+ export declare function continueDelegatedFlow(redirectTo: string, opts?: {
91
+ allowedOrigins?: string[];
92
+ navigate?: (url: string) => void;
93
+ }): void;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * Bring-your-own login and consent pages. Atlas sends the browser to your page
4
+ * with `?login_challenge=` or `?consent_challenge=`. Reading, accepting and
5
+ * rejecting the challenge needs a secret key, so your page talks to YOUR
6
+ * backend (see `delegatedUiProxy` in @atlasauth/backend), and then follows the
7
+ * `redirect_to` Atlas returned.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.readDelegatedChallenge = readDelegatedChallenge;
11
+ exports.createDelegatedUiClient = createDelegatedUiClient;
12
+ exports.continueDelegatedFlow = continueDelegatedFlow;
13
+ /** The challenge on the current page URL, or null when the page was not reached from Atlas. */
14
+ function readDelegatedChallenge(href) {
15
+ const url = new URL(typeof href === 'string' ? href : href instanceof URL ? href.href : href.href);
16
+ const login = url.searchParams.get('login_challenge');
17
+ if (login)
18
+ return { type: 'login', challenge: login };
19
+ const consent = url.searchParams.get('consent_challenge');
20
+ return consent ? { type: 'consent', challenge: consent } : null;
21
+ }
22
+ /**
23
+ * Calls your backend's proxy routes (`GET {baseUrl}/login`, `POST {baseUrl}/login/accept`, …),
24
+ * each taking the challenge as a query parameter. Errors throw with the server's message.
25
+ */
26
+ function createDelegatedUiClient(opts) {
27
+ const f = opts.fetch ?? ((...a) => fetch(...a));
28
+ const base = opts.baseUrl.replace(/\/$/, '');
29
+ async function call(method, path, kind, challenge, body) {
30
+ const res = await f(`${base}/${path}?${kind}_challenge=${encodeURIComponent(challenge)}`, {
31
+ method,
32
+ credentials: 'same-origin',
33
+ headers: { ...(body !== undefined ? { 'content-type': 'application/json' } : {}), ...opts.headers },
34
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
35
+ });
36
+ const json = (await res.json().catch(() => ({})));
37
+ if (!res.ok)
38
+ throw new Error(json.error?.message ?? json.message ?? `Request failed (${res.status}).`);
39
+ return json;
40
+ }
41
+ return {
42
+ getLoginRequest: (challenge) => call('GET', 'login', 'login', challenge),
43
+ /** `subject` is the Atlas user id your backend authenticated the visitor as. */
44
+ acceptLogin: (challenge, body) => call('POST', 'login/accept', 'login', challenge, body),
45
+ rejectLogin: (challenge, body = {}) => call('POST', 'login/reject', 'login', challenge, body),
46
+ getConsentRequest: (challenge) => call('GET', 'consent', 'consent', challenge),
47
+ acceptConsent: (challenge, body = {}) => call('POST', 'consent/accept', 'consent', challenge, body),
48
+ rejectConsent: (challenge, body = {}) => call('POST', 'consent/reject', 'consent', challenge, body),
49
+ };
50
+ }
51
+ /**
52
+ * Send the browser on to the `redirect_to` Atlas returned. Refuses anything that
53
+ * is not https (or an allowed origin), so a tampered response cannot redirect elsewhere.
54
+ */
55
+ function continueDelegatedFlow(redirectTo, opts = {}) {
56
+ const url = new URL(redirectTo);
57
+ const ok = opts.allowedOrigins ? opts.allowedOrigins.includes(url.origin) : url.protocol === 'https:';
58
+ if (!ok)
59
+ throw new Error('Refusing to follow an unexpected redirect.');
60
+ (opts.navigate ?? ((u) => window.location.assign(u)))(url.toString());
61
+ }
62
+ //# sourceMappingURL=delegated-ui.js.map