@eetr/eetr-auth-client 0.3.1 → 0.5.2
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/LICENSE +21 -0
- package/README.md +350 -0
- package/dist/admin.d.ts +82 -0
- package/dist/admin.d.ts.map +1 -1
- package/dist/admin.js +96 -0
- package/dist/admin.js.map +1 -1
- package/dist/api.d.ts +130 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +120 -4
- package/dist/api.js.map +1 -1
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts +11 -1
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js +13 -0
- package/dist/jwt.js.map +1 -1
- package/dist/profile.d.ts +14 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +23 -0
- package/dist/profile.js.map +1 -0
- package/dist/scopes.d.ts +25 -0
- package/dist/scopes.d.ts.map +1 -0
- package/dist/scopes.js +43 -0
- package/dist/scopes.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +32 -2
package/dist/api.d.ts
CHANGED
|
@@ -1,31 +1,160 @@
|
|
|
1
1
|
import type { TokenResponse, UserInfoResponse, TokenValidationResponse } from "./types.js";
|
|
2
2
|
export declare class OAuthError extends Error {
|
|
3
3
|
readonly code: string;
|
|
4
|
-
|
|
4
|
+
/** HTTP status of the failed response, when the error came from one. */
|
|
5
|
+
readonly status?: number | undefined;
|
|
6
|
+
/** The server's `error_description`, when present. */
|
|
7
|
+
readonly description?: string | undefined;
|
|
8
|
+
constructor(code: string, message: string,
|
|
9
|
+
/** HTTP status of the failed response, when the error came from one. */
|
|
10
|
+
status?: number | undefined,
|
|
11
|
+
/** The server's `error_description`, when present. */
|
|
12
|
+
description?: string | undefined);
|
|
13
|
+
/**
|
|
14
|
+
* True when the access token was valid but lacked a required scope (HTTP 403
|
|
15
|
+
* `insufficient_scope`) — e.g. calling `/userinfo` with a token that was not
|
|
16
|
+
* granted `openid`. Re-authorize requesting the missing scope rather than
|
|
17
|
+
* treating the token as invalid.
|
|
18
|
+
*/
|
|
19
|
+
get isInsufficientScope(): boolean;
|
|
5
20
|
}
|
|
6
21
|
export type GrantType = "authorization_code" | "client_credentials" | "refresh_token";
|
|
22
|
+
export interface AuthorizationUrlParams {
|
|
23
|
+
clientId: string;
|
|
24
|
+
redirectUri: string;
|
|
25
|
+
/** PKCE S256 challenge (required by this server). */
|
|
26
|
+
codeChallenge: string;
|
|
27
|
+
/** Space-delimited scope string. Merged with `scopes` if both are given. */
|
|
28
|
+
scope?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Scopes as an array (e.g. `[OIDCScope.OpenId, OIDCScope.Profile]`). Merged
|
|
31
|
+
* with `scope`, de-duplicated. Prefer this with the `OIDCScope` constants so
|
|
32
|
+
* `openid` can't be dropped by a typo.
|
|
33
|
+
*/
|
|
34
|
+
scopes?: readonly string[];
|
|
35
|
+
state?: string;
|
|
36
|
+
/** OIDC nonce, bound into the issued id_token for replay protection. */
|
|
37
|
+
nonce?: string;
|
|
38
|
+
/**
|
|
39
|
+
* RFC 8707 resource indicator: the protected-resource URL the token is for. The server
|
|
40
|
+
* binds the access token's `aud` to this value (instead of the client_id). Pass the same
|
|
41
|
+
* value to {@link exchangeToken}.
|
|
42
|
+
*/
|
|
43
|
+
resource?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build an authorization-request URL for the authorization_code + PKCE flow.
|
|
47
|
+
* Includes the OIDC `nonce` and RFC 8707 `resource` when provided.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildAuthorizationUrl(authorizationEndpoint: string, params: AuthorizationUrlParams): string;
|
|
7
50
|
export interface ExchangeTokenParams {
|
|
8
51
|
grantType: GrantType;
|
|
9
52
|
clientId: string;
|
|
10
53
|
clientSecret?: string;
|
|
54
|
+
/** Space-delimited scope string. Merged with `scopes` if both are given. */
|
|
11
55
|
scope?: string;
|
|
56
|
+
/** Scopes as an array (e.g. with the `OIDCScope` constants). Merged with `scope`. */
|
|
57
|
+
scopes?: readonly string[];
|
|
12
58
|
code?: string;
|
|
13
59
|
redirectUri?: string;
|
|
14
60
|
codeVerifier?: string;
|
|
15
61
|
refreshToken?: string;
|
|
62
|
+
/**
|
|
63
|
+
* RFC 8707 resource indicator. When provided it must match the value sent to
|
|
64
|
+
* {@link buildAuthorizationUrl} (the server rejects a mismatch with `invalid_target`).
|
|
65
|
+
*/
|
|
66
|
+
resource?: string;
|
|
16
67
|
}
|
|
17
68
|
export interface ExchangeTokenConfig {
|
|
18
69
|
tokenEndpoint: string;
|
|
19
70
|
}
|
|
20
71
|
export declare function exchangeToken(params: ExchangeTokenParams, config: ExchangeTokenConfig): Promise<TokenResponse>;
|
|
72
|
+
export interface ExchangeApiKeyParams {
|
|
73
|
+
/** The full credential, `eak_<keyId>_<secret>`. */
|
|
74
|
+
apiKey: string;
|
|
75
|
+
/**
|
|
76
|
+
* Space-delimited subset of the key's scopes. Omit for all of them. Scopes outside
|
|
77
|
+
* the key's set are rejected, never granted.
|
|
78
|
+
*/
|
|
79
|
+
scope?: string;
|
|
80
|
+
/** Scopes as an array. Merged with `scope`. */
|
|
81
|
+
scopes?: readonly string[];
|
|
82
|
+
/** RFC 8707 resource indicator; becomes the token's audience. */
|
|
83
|
+
resource?: string;
|
|
84
|
+
}
|
|
85
|
+
export interface ExchangeApiKeyConfig {
|
|
86
|
+
/** Usually `${issuer}/api/token/api-key`. */
|
|
87
|
+
apiKeyEndpoint: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Exchange a long-lived API key for a short-lived access token.
|
|
91
|
+
*
|
|
92
|
+
* Intended for CI/CD and other machine callers: one durable secret in the environment
|
|
93
|
+
* instead of a client_id + client_secret pair, and no client_credentials round trip
|
|
94
|
+
* before every request.
|
|
95
|
+
*
|
|
96
|
+
* The returned token carries the user the key is bound to as its `sub`. There is no
|
|
97
|
+
* `refresh_token` — the API key is itself the long-lived credential.
|
|
98
|
+
*/
|
|
99
|
+
export declare function exchangeApiKey(params: ExchangeApiKeyParams, config: ExchangeApiKeyConfig): Promise<TokenResponse>;
|
|
21
100
|
export interface IntrospectTokenParams {
|
|
22
101
|
token: string;
|
|
23
102
|
scopes?: string[];
|
|
24
103
|
environmentName: string;
|
|
104
|
+
/**
|
|
105
|
+
* Optional audience binding. When set to the calling resource server's own
|
|
106
|
+
* client_id, the token is only reported valid if it was issued to that client —
|
|
107
|
+
* preventing a token minted for a sibling client in the same environment from
|
|
108
|
+
* being accepted here. Omit to keep the previous (audience-agnostic) behavior.
|
|
109
|
+
*/
|
|
110
|
+
clientId?: string;
|
|
25
111
|
}
|
|
26
112
|
export interface IntrospectTokenConfig {
|
|
27
113
|
introspectionEndpoint: string;
|
|
28
114
|
}
|
|
29
115
|
export declare function introspectToken(params: IntrospectTokenParams, config: IntrospectTokenConfig): Promise<TokenValidationResponse>;
|
|
116
|
+
export interface RegisterClientParams {
|
|
117
|
+
/** Human-readable name shown on the consent screen. */
|
|
118
|
+
clientName?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Required. Exact-match https URLs; http is allowed only on a loopback host
|
|
121
|
+
* (`localhost`, any 127.0.0.0/8 address, or `[::1]`). At `/authorize` the loopback host is
|
|
122
|
+
* matched interchangeably — scheme, port, path, and query still have to match exactly.
|
|
123
|
+
*/
|
|
124
|
+
redirectUris: string[];
|
|
125
|
+
/** Defaults server-side to `none` (public/PKCE). Use a confidential method for a secret. */
|
|
126
|
+
tokenEndpointAuthMethod?: "none" | "client_secret_basic" | "client_secret_post";
|
|
127
|
+
/** Defaults server-side to `["authorization_code", "refresh_token"]`. */
|
|
128
|
+
grantTypes?: string[];
|
|
129
|
+
/** Defaults server-side to `["code"]`. */
|
|
130
|
+
responseTypes?: string[];
|
|
131
|
+
/** Space-delimited scope string. Merged with `scopes` if both are given. */
|
|
132
|
+
scope?: string;
|
|
133
|
+
/** Scopes as an array (e.g. with the `OIDCScope` constants). Merged with `scope`. */
|
|
134
|
+
scopes?: readonly string[];
|
|
135
|
+
}
|
|
136
|
+
export interface RegisterClientConfig {
|
|
137
|
+
/** The `registration_endpoint` from discovery metadata. */
|
|
138
|
+
registrationEndpoint: string;
|
|
139
|
+
}
|
|
140
|
+
/** RFC 7591 client registration response. `client_secret` is present only for confidential clients. */
|
|
141
|
+
export interface RegisterClientResponse {
|
|
142
|
+
client_id: string;
|
|
143
|
+
client_id_issued_at: number;
|
|
144
|
+
redirect_uris: string[];
|
|
145
|
+
grant_types: string[];
|
|
146
|
+
response_types: string[];
|
|
147
|
+
token_endpoint_auth_method: string;
|
|
148
|
+
scope?: string;
|
|
149
|
+
client_secret?: string;
|
|
150
|
+
client_secret_expires_at?: number;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Dynamically register an OAuth client (RFC 7591) at the server's `registration_endpoint`.
|
|
154
|
+
* Public clients (`tokenEndpointAuthMethod: "none"`, the default) get no secret and use PKCE.
|
|
155
|
+
* Throws {@link OAuthError} on a non-2xx response (e.g. `invalid_redirect_uri`,
|
|
156
|
+
* `invalid_client_metadata`, or `too_many_requests` when rate-limited).
|
|
157
|
+
*/
|
|
158
|
+
export declare function registerClient(params: RegisterClientParams, config: RegisterClientConfig): Promise<RegisterClientResponse>;
|
|
30
159
|
export declare function getUserInfo(accessToken: string, userInfoEndpoint: string): Promise<UserInfoResponse>;
|
|
31
160
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
|
+
import { resolveScopeParam } from "./scopes.js";
|
|
1
2
|
export class OAuthError extends Error {
|
|
2
3
|
code;
|
|
3
|
-
|
|
4
|
+
status;
|
|
5
|
+
description;
|
|
6
|
+
constructor(code, message,
|
|
7
|
+
/** HTTP status of the failed response, when the error came from one. */
|
|
8
|
+
status,
|
|
9
|
+
/** The server's `error_description`, when present. */
|
|
10
|
+
description) {
|
|
4
11
|
super(message);
|
|
5
12
|
this.code = code;
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.description = description;
|
|
6
15
|
this.name = "OAuthError";
|
|
7
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* True when the access token was valid but lacked a required scope (HTTP 403
|
|
19
|
+
* `insufficient_scope`) — e.g. calling `/userinfo` with a token that was not
|
|
20
|
+
* granted `openid`. Re-authorize requesting the missing scope rather than
|
|
21
|
+
* treating the token as invalid.
|
|
22
|
+
*/
|
|
23
|
+
get isInsufficientScope() {
|
|
24
|
+
return this.code === "insufficient_scope";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build an authorization-request URL for the authorization_code + PKCE flow.
|
|
29
|
+
* Includes the OIDC `nonce` and RFC 8707 `resource` when provided.
|
|
30
|
+
*/
|
|
31
|
+
export function buildAuthorizationUrl(authorizationEndpoint, params) {
|
|
32
|
+
const url = new URL(authorizationEndpoint);
|
|
33
|
+
url.searchParams.set("response_type", "code");
|
|
34
|
+
url.searchParams.set("client_id", params.clientId);
|
|
35
|
+
url.searchParams.set("redirect_uri", params.redirectUri);
|
|
36
|
+
url.searchParams.set("code_challenge", params.codeChallenge);
|
|
37
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
38
|
+
const scope = resolveScopeParam(params.scope, params.scopes);
|
|
39
|
+
if (scope)
|
|
40
|
+
url.searchParams.set("scope", scope);
|
|
41
|
+
if (params.state)
|
|
42
|
+
url.searchParams.set("state", params.state);
|
|
43
|
+
if (params.nonce)
|
|
44
|
+
url.searchParams.set("nonce", params.nonce);
|
|
45
|
+
if (params.resource)
|
|
46
|
+
url.searchParams.set("resource", params.resource);
|
|
47
|
+
return url.toString();
|
|
8
48
|
}
|
|
9
49
|
export async function exchangeToken(params, config) {
|
|
10
50
|
const body = new URLSearchParams();
|
|
@@ -12,8 +52,9 @@ export async function exchangeToken(params, config) {
|
|
|
12
52
|
body.set("client_id", params.clientId);
|
|
13
53
|
if (params.clientSecret)
|
|
14
54
|
body.set("client_secret", params.clientSecret);
|
|
15
|
-
|
|
16
|
-
|
|
55
|
+
const scope = resolveScopeParam(params.scope, params.scopes);
|
|
56
|
+
if (scope)
|
|
57
|
+
body.set("scope", scope);
|
|
17
58
|
if (params.code)
|
|
18
59
|
body.set("code", params.code);
|
|
19
60
|
if (params.redirectUri)
|
|
@@ -22,6 +63,8 @@ export async function exchangeToken(params, config) {
|
|
|
22
63
|
body.set("code_verifier", params.codeVerifier);
|
|
23
64
|
if (params.refreshToken)
|
|
24
65
|
body.set("refresh_token", params.refreshToken);
|
|
66
|
+
if (params.resource)
|
|
67
|
+
body.set("resource", params.resource);
|
|
25
68
|
const res = await fetch(config.tokenEndpoint, {
|
|
26
69
|
method: "POST",
|
|
27
70
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -33,6 +76,39 @@ export async function exchangeToken(params, config) {
|
|
|
33
76
|
}
|
|
34
77
|
return data;
|
|
35
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Exchange a long-lived API key for a short-lived access token.
|
|
81
|
+
*
|
|
82
|
+
* Intended for CI/CD and other machine callers: one durable secret in the environment
|
|
83
|
+
* instead of a client_id + client_secret pair, and no client_credentials round trip
|
|
84
|
+
* before every request.
|
|
85
|
+
*
|
|
86
|
+
* The returned token carries the user the key is bound to as its `sub`. There is no
|
|
87
|
+
* `refresh_token` — the API key is itself the long-lived credential.
|
|
88
|
+
*/
|
|
89
|
+
export async function exchangeApiKey(params, config) {
|
|
90
|
+
const body = new URLSearchParams();
|
|
91
|
+
const scope = resolveScopeParam(params.scope, params.scopes);
|
|
92
|
+
if (scope)
|
|
93
|
+
body.set("scope", scope);
|
|
94
|
+
if (params.resource)
|
|
95
|
+
body.set("resource", params.resource);
|
|
96
|
+
const res = await fetch(config.apiKeyEndpoint, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
// Sent as a bearer credential rather than in the body so the key stays out of
|
|
100
|
+
// anything that logs request bodies.
|
|
101
|
+
Authorization: `Bearer ${params.apiKey}`,
|
|
102
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
103
|
+
},
|
|
104
|
+
body,
|
|
105
|
+
});
|
|
106
|
+
const data = (await res.json());
|
|
107
|
+
if (!res.ok) {
|
|
108
|
+
throw new OAuthError(data.error ?? "server_error", data.error_description ?? `API key exchange failed: ${res.status}`, res.status, data.error_description);
|
|
109
|
+
}
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
36
112
|
export async function introspectToken(params, config) {
|
|
37
113
|
const res = await fetch(config.introspectionEndpoint, {
|
|
38
114
|
method: "POST",
|
|
@@ -41,17 +117,57 @@ export async function introspectToken(params, config) {
|
|
|
41
117
|
token: params.token,
|
|
42
118
|
scopes: params.scopes ?? [],
|
|
43
119
|
environmentName: params.environmentName,
|
|
120
|
+
...(params.clientId ? { clientId: params.clientId } : {}),
|
|
44
121
|
}),
|
|
45
122
|
});
|
|
46
123
|
return res.json();
|
|
47
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Dynamically register an OAuth client (RFC 7591) at the server's `registration_endpoint`.
|
|
127
|
+
* Public clients (`tokenEndpointAuthMethod: "none"`, the default) get no secret and use PKCE.
|
|
128
|
+
* Throws {@link OAuthError} on a non-2xx response (e.g. `invalid_redirect_uri`,
|
|
129
|
+
* `invalid_client_metadata`, or `too_many_requests` when rate-limited).
|
|
130
|
+
*/
|
|
131
|
+
export async function registerClient(params, config) {
|
|
132
|
+
const requestBody = { redirect_uris: params.redirectUris };
|
|
133
|
+
if (params.clientName)
|
|
134
|
+
requestBody.client_name = params.clientName;
|
|
135
|
+
if (params.tokenEndpointAuthMethod)
|
|
136
|
+
requestBody.token_endpoint_auth_method = params.tokenEndpointAuthMethod;
|
|
137
|
+
if (params.grantTypes)
|
|
138
|
+
requestBody.grant_types = params.grantTypes;
|
|
139
|
+
if (params.responseTypes)
|
|
140
|
+
requestBody.response_types = params.responseTypes;
|
|
141
|
+
const scope = resolveScopeParam(params.scope, params.scopes);
|
|
142
|
+
if (scope)
|
|
143
|
+
requestBody.scope = scope;
|
|
144
|
+
const res = await fetch(config.registrationEndpoint, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: { "Content-Type": "application/json" },
|
|
147
|
+
body: JSON.stringify(requestBody),
|
|
148
|
+
});
|
|
149
|
+
const data = (await res.json().catch(() => ({})));
|
|
150
|
+
if (!res.ok) {
|
|
151
|
+
throw new OAuthError(data.error ?? "server_error", data.error_description ?? `Client registration failed: ${res.status}`, res.status, data.error_description);
|
|
152
|
+
}
|
|
153
|
+
return data;
|
|
154
|
+
}
|
|
48
155
|
export async function getUserInfo(accessToken, userInfoEndpoint) {
|
|
49
156
|
const res = await fetch(userInfoEndpoint, {
|
|
50
157
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
51
158
|
});
|
|
52
159
|
if (!res.ok) {
|
|
53
160
|
const data = (await res.json().catch(() => ({})));
|
|
54
|
-
|
|
161
|
+
// A 403 means the token is valid but missing a required scope (`openid`).
|
|
162
|
+
// Surface it as `insufficient_scope` so callers can re-authorize with the
|
|
163
|
+
// right scopes instead of discarding the token as invalid. A 401 (or a
|
|
164
|
+
// non-JSON body) stays `invalid_token`.
|
|
165
|
+
const code = data.error ?? (res.status === 403 ? "insufficient_scope" : "invalid_token");
|
|
166
|
+
const message = data.error_description ??
|
|
167
|
+
(code === "insufficient_scope"
|
|
168
|
+
? "UserInfo requires the openid scope on the access token."
|
|
169
|
+
: `UserInfo request failed: ${res.status}`);
|
|
170
|
+
throw new OAuthError(code, message, res.status, data.error_description);
|
|
55
171
|
}
|
|
56
172
|
return res.json();
|
|
57
173
|
}
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
export type { TokenResponse, UserInfoResponse, OIDCDiscovery, OAuthServerMetadata, AuthClientConfig, JWTPayload, TokenValidationResponse, } from "./types.js";
|
|
1
|
+
export type { TokenResponse, UserInfoResponse, UserProfile, OIDCDiscovery, OAuthServerMetadata, AuthClientConfig, JWTPayload, IDTokenClaims, TokenValidationResponse, } from "./types.js";
|
|
2
|
+
export { OIDCScope, STANDARD_OIDC_SCOPES, resolveScopeParam, } from "./scopes.js";
|
|
3
|
+
export type { OIDCScopeValue } from "./scopes.js";
|
|
4
|
+
export { toUserProfile } from "./profile.js";
|
|
2
5
|
export { fetchOIDCDiscovery, fetchOAuthMetadata } from "./discovery.js";
|
|
3
|
-
export { OAuthError, exchangeToken, introspectToken, getUserInfo, } from "./api.js";
|
|
4
|
-
export type { GrantType, ExchangeTokenParams, ExchangeTokenConfig, IntrospectTokenParams, IntrospectTokenConfig, } from "./api.js";
|
|
6
|
+
export { OAuthError, exchangeToken, introspectToken, getUserInfo, buildAuthorizationUrl, registerClient, exchangeApiKey, } from "./api.js";
|
|
7
|
+
export type { GrantType, ExchangeTokenParams, ExchangeTokenConfig, IntrospectTokenParams, IntrospectTokenConfig, AuthorizationUrlParams, RegisterClientParams, RegisterClientConfig, RegisterClientResponse, ExchangeApiKeyParams, ExchangeApiKeyConfig, } from "./api.js";
|
|
5
8
|
export { TokenManager } from "./tokens.js";
|
|
6
|
-
export { validateJwt, decodeJwtPayload } from "./jwt.js";
|
|
7
|
-
export type { ValidateJwtOptions } from "./jwt.js";
|
|
8
|
-
export { getAdminUser, createAdminUser, updateAdminUser, deleteAdminUser, } from "./admin.js";
|
|
9
|
-
export type { AdminUserRecord, AdminClientConfig, CreateUserParams, UpdateUserParams, } from "./admin.js";
|
|
9
|
+
export { validateJwt, validateIdToken, decodeJwtPayload } from "./jwt.js";
|
|
10
|
+
export type { ValidateJwtOptions, ValidateIdTokenOptions } from "./jwt.js";
|
|
11
|
+
export { getAdminUser, createAdminUser, updateAdminUser, deleteAdminUser, listUserConsents, revokeUserConsent, listClientApiKeys, createClientApiKey, revokeClientApiKey, } from "./admin.js";
|
|
12
|
+
export type { AdminUserRecord, AdminClientConfig, AdminConsentRecord, RevokeConsentResult, CreateUserParams, UpdateUserParams, ApiKeyRecord, CreateApiKeyParams, CreateApiKeyResult, } from "./admin.js";
|
|
10
13
|
export { listPasskeys, renamePasskey, removePasskey } from "./passkeys.js";
|
|
11
14
|
export type { PasskeySummary, UserClientConfig } from "./passkeys.js";
|
|
12
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,uBAAuB,GACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,uBAAuB,GACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAE3E,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3E,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
export { OIDCScope, STANDARD_OIDC_SCOPES, resolveScopeParam, } from "./scopes.js";
|
|
2
|
+
export { toUserProfile } from "./profile.js";
|
|
1
3
|
export { fetchOIDCDiscovery, fetchOAuthMetadata } from "./discovery.js";
|
|
2
|
-
export { OAuthError, exchangeToken, introspectToken, getUserInfo, } from "./api.js";
|
|
4
|
+
export { OAuthError, exchangeToken, introspectToken, getUserInfo, buildAuthorizationUrl, registerClient, exchangeApiKey, } from "./api.js";
|
|
3
5
|
export { TokenManager } from "./tokens.js";
|
|
4
|
-
export { validateJwt, decodeJwtPayload } from "./jwt.js";
|
|
5
|
-
export { getAdminUser, createAdminUser, updateAdminUser, deleteAdminUser, } from "./admin.js";
|
|
6
|
+
export { validateJwt, validateIdToken, decodeJwtPayload } from "./jwt.js";
|
|
7
|
+
export { getAdminUser, createAdminUser, updateAdminUser, deleteAdminUser, listUserConsents, revokeUserConsent, listClientApiKeys, createClientApiKey, revokeClientApiKey, } from "./admin.js";
|
|
6
8
|
export { listPasskeys, renamePasskey, removePasskey } from "./passkeys.js";
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,cAAc,GACf,MAAM,UAAU,CAAC;AAelB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG1E,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAapB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/jwt.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import type { JWTPayload } from "./types.js";
|
|
1
|
+
import type { IDTokenClaims, JWTPayload } from "./types.js";
|
|
2
2
|
export interface ValidateJwtOptions {
|
|
3
3
|
audience?: string | string[];
|
|
4
4
|
issuer?: string;
|
|
5
5
|
clockTolerance?: number;
|
|
6
6
|
}
|
|
7
|
+
export interface ValidateIdTokenOptions extends ValidateJwtOptions {
|
|
8
|
+
/** When set, the id_token's `nonce` claim must match this value (OIDC replay defense). */
|
|
9
|
+
nonce?: string;
|
|
10
|
+
}
|
|
7
11
|
export declare function validateJwt(token: string, jwksUri: string, options?: ValidateJwtOptions): Promise<JWTPayload>;
|
|
12
|
+
/**
|
|
13
|
+
* Verify an OIDC id_token: checks the RS256 signature against the JWKS plus
|
|
14
|
+
* `iss`/`aud`/`exp` (via {@link validateJwt}), and — when `options.nonce` is
|
|
15
|
+
* supplied — that the token's `nonce` claim matches, defending against replay.
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateIdToken(token: string, jwksUri: string, options?: ValidateIdTokenOptions): Promise<IDTokenClaims>;
|
|
8
18
|
export declare function decodeJwtPayload(token: string): JWTPayload;
|
|
9
19
|
//# sourceMappingURL=jwt.d.ts.map
|
package/dist/jwt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,UAAU,CAAC,CAQrB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,aAAa,CAAC,CAOxB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAQ1D"}
|
package/dist/jwt.js
CHANGED
|
@@ -15,6 +15,19 @@ export async function validateJwt(token, jwksUri, options = {}) {
|
|
|
15
15
|
});
|
|
16
16
|
return payload;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Verify an OIDC id_token: checks the RS256 signature against the JWKS plus
|
|
20
|
+
* `iss`/`aud`/`exp` (via {@link validateJwt}), and — when `options.nonce` is
|
|
21
|
+
* supplied — that the token's `nonce` claim matches, defending against replay.
|
|
22
|
+
*/
|
|
23
|
+
export async function validateIdToken(token, jwksUri, options = {}) {
|
|
24
|
+
const { nonce, ...jwtOptions } = options;
|
|
25
|
+
const payload = (await validateJwt(token, jwksUri, jwtOptions));
|
|
26
|
+
if (nonce !== undefined && payload.nonce !== nonce) {
|
|
27
|
+
throw new Error("id_token nonce mismatch");
|
|
28
|
+
}
|
|
29
|
+
return payload;
|
|
30
|
+
}
|
|
18
31
|
export function decodeJwtPayload(token) {
|
|
19
32
|
const parts = token.split(".");
|
|
20
33
|
if (parts.length !== 3) {
|
package/dist/jwt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAcrD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAiD,CAAC;AAE3E,SAAS,OAAO,CAAC,OAAe;IAC9B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,kBAAkB,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,SAAS,CAAC,GAAG,CAAC,OAAO,CAAE,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,OAAe,EACf,UAA8B,EAAE;IAEhC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;QAC/C,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,CAAC;KAC5C,CAAC,CAAC;IACH,OAAO,OAAqB,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,OAAe,EACf,UAAkC,EAAE;IAEpC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,CAAkB,CAAC;IACjF,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IDTokenClaims, UserInfoResponse, UserProfile } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Merge `/userinfo` claims (and optionally id_token claims) into a normalized,
|
|
4
|
+
* camelCased {@link UserProfile}. UserInfo values take precedence over id_token
|
|
5
|
+
* values — UserInfo is the authoritative, freshest source — and the id_token
|
|
6
|
+
* fills any gap for claims `/userinfo` did not return.
|
|
7
|
+
*
|
|
8
|
+
* Fields stay optional because the server only returns claims the access token's
|
|
9
|
+
* scopes allow (`profile` → name/preferredUsername/picture, `email` →
|
|
10
|
+
* email/emailVerified), so a profile built from an `openid`-only token carries
|
|
11
|
+
* just `sub`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function toUserProfile(userInfo: UserInfoResponse, idTokenClaims?: IDTokenClaims): UserProfile;
|
|
14
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,CAAC,EAAE,aAAa,GAC5B,WAAW,CAcb"}
|
package/dist/profile.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge `/userinfo` claims (and optionally id_token claims) into a normalized,
|
|
3
|
+
* camelCased {@link UserProfile}. UserInfo values take precedence over id_token
|
|
4
|
+
* values — UserInfo is the authoritative, freshest source — and the id_token
|
|
5
|
+
* fills any gap for claims `/userinfo` did not return.
|
|
6
|
+
*
|
|
7
|
+
* Fields stay optional because the server only returns claims the access token's
|
|
8
|
+
* scopes allow (`profile` → name/preferredUsername/picture, `email` →
|
|
9
|
+
* email/emailVerified), so a profile built from an `openid`-only token carries
|
|
10
|
+
* just `sub`.
|
|
11
|
+
*/
|
|
12
|
+
export function toUserProfile(userInfo, idTokenClaims) {
|
|
13
|
+
const prefer = (primary, fallback) => primary !== undefined ? primary : fallback;
|
|
14
|
+
return {
|
|
15
|
+
sub: userInfo.sub ?? idTokenClaims?.sub ?? "",
|
|
16
|
+
name: prefer(userInfo.name, idTokenClaims?.name),
|
|
17
|
+
preferredUsername: prefer(userInfo.preferred_username, idTokenClaims?.preferred_username),
|
|
18
|
+
picture: prefer(userInfo.picture, idTokenClaims?.picture),
|
|
19
|
+
email: prefer(userInfo.email, idTokenClaims?.email),
|
|
20
|
+
emailVerified: prefer(userInfo.email_verified, idTokenClaims?.email_verified),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,QAA0B,EAC1B,aAA6B;IAE7B,MAAM,MAAM,GAAG,CAAI,OAAsB,EAAE,QAAuB,EAAiB,EAAE,CACnF,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC7C,OAAO;QACL,GAAG,EAAE,QAAQ,CAAC,GAAG,IAAI,aAAa,EAAE,GAAG,IAAI,EAAE;QAC7C,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC;QAChD,iBAAiB,EAAE,MAAM,CACvB,QAAQ,CAAC,kBAAkB,EAC3B,aAAa,EAAE,kBAAkB,CAClC;QACD,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC;QACzD,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC;QACnD,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE,aAAa,EAAE,cAAc,CAAC;KAC9E,CAAC;AACJ,CAAC"}
|
package/dist/scopes.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard OpenID Connect scopes recognized by the eetr-auth server. These are seeded
|
|
3
|
+
* on install; an admin still grants them to each client. Use them when building an
|
|
4
|
+
* authorization request or token exchange so a typo can't silently drop `openid` (which
|
|
5
|
+
* would make `/userinfo` return 403 insufficient_scope).
|
|
6
|
+
*/
|
|
7
|
+
export declare const OIDCScope: {
|
|
8
|
+
/** Required to mint an id_token and to call the `/userinfo` endpoint. */
|
|
9
|
+
readonly OpenId: "openid";
|
|
10
|
+
/** Gates the `name` / `preferred_username` / `picture` claims. */
|
|
11
|
+
readonly Profile: "profile";
|
|
12
|
+
/** Gates the `email` / `email_verified` claims. */
|
|
13
|
+
readonly Email: "email";
|
|
14
|
+
};
|
|
15
|
+
export type OIDCScopeValue = (typeof OIDCScope)[keyof typeof OIDCScope];
|
|
16
|
+
/** The standard OIDC scopes, in canonical order: `openid profile email`. */
|
|
17
|
+
export declare const STANDARD_OIDC_SCOPES: readonly OIDCScopeValue[];
|
|
18
|
+
/**
|
|
19
|
+
* Merge a free-form `scope` string and/or a `scopes` array into a single
|
|
20
|
+
* space-delimited scope string, trimmed, de-duplicated, and order-preserving
|
|
21
|
+
* (the `scope` string first, then `scopes`). Returns `undefined` when neither
|
|
22
|
+
* yields any scope, so callers can omit the parameter entirely.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveScopeParam(scope?: string, scopes?: readonly string[]): string | undefined;
|
|
25
|
+
//# sourceMappingURL=scopes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../src/scopes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,SAAS;IACpB,yEAAyE;;IAEzE,kEAAkE;;IAElE,mDAAmD;;CAE3C,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAExE,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,EAAE,SAAS,cAAc,EAIzD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,GAAG,SAAS,CAapB"}
|
package/dist/scopes.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard OpenID Connect scopes recognized by the eetr-auth server. These are seeded
|
|
3
|
+
* on install; an admin still grants them to each client. Use them when building an
|
|
4
|
+
* authorization request or token exchange so a typo can't silently drop `openid` (which
|
|
5
|
+
* would make `/userinfo` return 403 insufficient_scope).
|
|
6
|
+
*/
|
|
7
|
+
export const OIDCScope = {
|
|
8
|
+
/** Required to mint an id_token and to call the `/userinfo` endpoint. */
|
|
9
|
+
OpenId: "openid",
|
|
10
|
+
/** Gates the `name` / `preferred_username` / `picture` claims. */
|
|
11
|
+
Profile: "profile",
|
|
12
|
+
/** Gates the `email` / `email_verified` claims. */
|
|
13
|
+
Email: "email",
|
|
14
|
+
};
|
|
15
|
+
/** The standard OIDC scopes, in canonical order: `openid profile email`. */
|
|
16
|
+
export const STANDARD_OIDC_SCOPES = [
|
|
17
|
+
OIDCScope.OpenId,
|
|
18
|
+
OIDCScope.Profile,
|
|
19
|
+
OIDCScope.Email,
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Merge a free-form `scope` string and/or a `scopes` array into a single
|
|
23
|
+
* space-delimited scope string, trimmed, de-duplicated, and order-preserving
|
|
24
|
+
* (the `scope` string first, then `scopes`). Returns `undefined` when neither
|
|
25
|
+
* yields any scope, so callers can omit the parameter entirely.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveScopeParam(scope, scopes) {
|
|
28
|
+
const out = [];
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
const add = (value) => {
|
|
31
|
+
const trimmed = value.trim();
|
|
32
|
+
if (trimmed && !seen.has(trimmed)) {
|
|
33
|
+
seen.add(trimmed);
|
|
34
|
+
out.push(trimmed);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
if (scope)
|
|
38
|
+
scope.split(/\s+/).forEach(add);
|
|
39
|
+
if (scopes)
|
|
40
|
+
scopes.forEach(add);
|
|
41
|
+
return out.length > 0 ? out.join(" ") : undefined;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=scopes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopes.js","sourceRoot":"","sources":["../src/scopes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,yEAAyE;IACzE,MAAM,EAAE,QAAQ;IAChB,kEAAkE;IAClE,OAAO,EAAE,SAAS;IAClB,mDAAmD;IACnD,KAAK,EAAE,OAAO;CACN,CAAC;AAIX,4EAA4E;AAC5E,MAAM,CAAC,MAAM,oBAAoB,GAA8B;IAC7D,SAAS,CAAC,MAAM;IAChB,SAAS,CAAC,OAAO;IACjB,SAAS,CAAC,KAAK;CAChB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAc,EACd,MAA0B;IAE1B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,EAAE;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IACF,IAAI,KAAK;QAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,MAAM;QAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC"}
|