@eventuras/fides-auth 0.5.0 → 0.7.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/logger.js +100 -58
- package/dist/logger.js.map +1 -0
- package/dist/oauth-browser.js +52 -30
- package/dist/oauth-browser.js.map +1 -0
- package/dist/oauth-logging.d.ts +45 -0
- package/dist/oauth-logging.d.ts.map +1 -0
- package/dist/oauth-logging.js +50 -0
- package/dist/oauth-logging.js.map +1 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +293 -133
- package/dist/oauth.js.map +1 -0
- package/dist/providers/vipps/index.js +162 -76
- package/dist/providers/vipps/index.js.map +1 -0
- package/dist/rate-limit.js +55 -17
- package/dist/rate-limit.js.map +1 -0
- package/dist/session-refresh.d.ts.map +1 -1
- package/dist/session-refresh.js +40 -19
- package/dist/session-refresh.js.map +1 -0
- package/dist/session-validation.js +77 -2
- package/dist/session-validation.js.map +1 -0
- package/dist/silent-login.js +133 -33
- package/dist/silent-login.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +196 -2
- package/dist/utils.js.map +1 -0
- package/package.json +6 -2
- package/dist/build-nL2--iMi.js +0 -1014
- package/dist/decode_jwt-1J26fl4I.js +0 -25
- package/dist/decrypt-Cahlu_6Y.js +0 -92
- package/dist/deflate-koSuX7FB.js +0 -1015
- package/dist/session-validation-BxObT3wC.js +0 -66
- package/dist/utils-ByMRF7b2.js +0 -379
package/dist/silent-login.js
CHANGED
|
@@ -1,50 +1,150 @@
|
|
|
1
|
-
import { createLogger
|
|
2
|
-
import
|
|
1
|
+
import { createLogger } from "./logger.js";
|
|
2
|
+
import * as openid from "openid-client";
|
|
3
3
|
//#region src/silent-login.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Silent login functionality for OAuth flows.
|
|
6
|
+
*
|
|
7
|
+
* Silent login (also called "prompt=none") attempts to authenticate the user
|
|
8
|
+
* without any user interaction. This is useful for:
|
|
9
|
+
* - Checking if a user has an active SSO session
|
|
10
|
+
* - Refreshing authentication silently in the background
|
|
11
|
+
* - Implementing "keep me logged in" features
|
|
12
|
+
*/
|
|
13
|
+
var logger = createLogger({ namespace: "fides-auth:silent-login" });
|
|
14
|
+
/**
|
|
15
|
+
* Builds an authorization URL for silent authentication.
|
|
16
|
+
*
|
|
17
|
+
* This function creates an authorization URL with prompt=none,
|
|
18
|
+
* which tells the OAuth provider to authenticate without user interaction.
|
|
19
|
+
* If the user doesn't have an active session, the provider will return an error.
|
|
20
|
+
*
|
|
21
|
+
* @param config - OAuth configuration
|
|
22
|
+
* @param options - Silent login options
|
|
23
|
+
* @returns Authorization URL for silent authentication
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const silentAuthUrl = await buildSilentLoginUrl(oauthConfig, {
|
|
28
|
+
* loginHint: 'user@example.com',
|
|
29
|
+
* maxAge: 3600, // Require authentication within last hour
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Use this URL in an iframe or hidden window
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
async function buildSilentLoginUrl(config, options = {}) {
|
|
36
|
+
logger.debug({ issuer: config.issuer }, "Building silent login URL");
|
|
7
37
|
try {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
38
|
+
const server = await openid.discovery(new URL(config.issuer), config.clientId, config.clientSecret, openid.ClientSecretPost(config.clientSecret));
|
|
39
|
+
const parameters = {
|
|
40
|
+
redirect_uri: config.redirect_uri,
|
|
41
|
+
scope: config.scope,
|
|
11
42
|
prompt: "none",
|
|
12
43
|
response_type: "code",
|
|
13
|
-
...
|
|
44
|
+
...options.additionalParams
|
|
14
45
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
46
|
+
if (options.loginHint) parameters.login_hint = options.loginHint;
|
|
47
|
+
if (options.maxAge !== void 0) parameters.max_age = options.maxAge.toString();
|
|
48
|
+
const authUrl = openid.buildAuthorizationUrl(server, parameters);
|
|
49
|
+
logger.info("Silent login URL built successfully");
|
|
50
|
+
return authUrl;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error({
|
|
53
|
+
error,
|
|
54
|
+
issuer: config.issuer
|
|
55
|
+
}, "Failed to build silent login URL");
|
|
56
|
+
throw error;
|
|
23
57
|
}
|
|
24
58
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Checks if silent login is possible by examining the callback URL.
|
|
61
|
+
*
|
|
62
|
+
* After redirecting to the silent login URL, the OAuth provider will redirect back
|
|
63
|
+
* with either a code (success) or an error (failure). This function parses the
|
|
64
|
+
* callback URL to determine the result.
|
|
65
|
+
*
|
|
66
|
+
* @param callbackUrl - The URL the OAuth provider redirected to
|
|
67
|
+
* @returns Result indicating success or failure with details
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* // In your OAuth callback handler
|
|
72
|
+
* const result = checkSilentLoginResult(new URL(request.url));
|
|
73
|
+
*
|
|
74
|
+
* if (result.success) {
|
|
75
|
+
* // User has active SSO session, proceed with token exchange
|
|
76
|
+
* const code = new URL(request.url).searchParams.get('code');
|
|
77
|
+
* // ... exchange code for tokens
|
|
78
|
+
* } else if (result.error === 'login_required') {
|
|
79
|
+
* // User needs to log in interactively
|
|
80
|
+
* redirect('/login');
|
|
81
|
+
* } else {
|
|
82
|
+
* // Other error
|
|
83
|
+
* console.error(result.errorDescription);
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
function checkSilentLoginResult(callbackUrl) {
|
|
88
|
+
const error = callbackUrl.searchParams.get("error");
|
|
89
|
+
const errorDescription = callbackUrl.searchParams.get("error_description");
|
|
90
|
+
const code = callbackUrl.searchParams.get("code");
|
|
91
|
+
if (error) {
|
|
92
|
+
logger.info({
|
|
93
|
+
error,
|
|
94
|
+
errorDescription
|
|
95
|
+
}, "Silent login failed with error");
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
error,
|
|
99
|
+
errorDescription: errorDescription ?? void 0
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (code) {
|
|
103
|
+
logger.info("Silent login successful");
|
|
104
|
+
return { success: true };
|
|
105
|
+
}
|
|
106
|
+
logger.warn("Silent login callback missing both code and error");
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
36
109
|
error: "invalid_callback",
|
|
37
110
|
errorDescription: "Callback URL missing both code and error parameters"
|
|
38
|
-
}
|
|
111
|
+
};
|
|
39
112
|
}
|
|
40
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Common OAuth errors returned from silent login attempts.
|
|
115
|
+
*
|
|
116
|
+
* These can be used to handle specific error cases:
|
|
117
|
+
* - login_required: User must log in interactively
|
|
118
|
+
* - interaction_required: User interaction is needed (e.g., consent)
|
|
119
|
+
* - consent_required: User must grant consent
|
|
120
|
+
* - account_selection_required: User must select an account
|
|
121
|
+
*/
|
|
122
|
+
var SilentLoginErrors = {
|
|
41
123
|
LOGIN_REQUIRED: "login_required",
|
|
42
124
|
INTERACTION_REQUIRED: "interaction_required",
|
|
43
125
|
CONSENT_REQUIRED: "consent_required",
|
|
44
126
|
ACCOUNT_SELECTION_REQUIRED: "account_selection_required"
|
|
45
127
|
};
|
|
46
|
-
|
|
47
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Helper to check if an error requires interactive login.
|
|
130
|
+
*
|
|
131
|
+
* @param error - The error code from the OAuth provider
|
|
132
|
+
* @returns True if the user must log in interactively
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const result = checkSilentLoginResult(callbackUrl);
|
|
137
|
+
* if (!result.success && requiresInteractiveLogin(result.error)) {
|
|
138
|
+
* // Redirect to full login page
|
|
139
|
+
* redirect('/login');
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
function requiresInteractiveLogin(error) {
|
|
144
|
+
if (!error) return false;
|
|
145
|
+
return Object.values(SilentLoginErrors).includes(error);
|
|
48
146
|
}
|
|
49
147
|
//#endregion
|
|
50
|
-
export {
|
|
148
|
+
export { SilentLoginErrors, buildSilentLoginUrl, checkSilentLoginResult, requiresInteractiveLogin };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=silent-login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"silent-login.js","names":[],"sources":["../src/silent-login.ts"],"sourcesContent":["/**\n * Silent login functionality for OAuth flows.\n *\n * Silent login (also called \"prompt=none\") attempts to authenticate the user\n * without any user interaction. This is useful for:\n * - Checking if a user has an active SSO session\n * - Refreshing authentication silently in the background\n * - Implementing \"keep me logged in\" features\n */\n\nimport * as openid from 'openid-client';\n\nimport { createLogger } from './logger';\nimport { OAuthConfig } from './oauth';\n\nconst logger = createLogger({ namespace: 'fides-auth:silent-login' });\n\nexport interface SilentLoginOptions {\n /** Optional hint about the user's identity (e.g., email or user ID) */\n loginHint?: string;\n /** Maximum age of authentication in seconds */\n maxAge?: number;\n /** Additional parameters to pass to the authorization endpoint */\n additionalParams?: Record<string, string>;\n}\n\nexport interface SilentLoginResult {\n /** Whether silent login was successful */\n success: boolean;\n /** The authorization URL to use (only if success is true) */\n authorizationUrl?: URL;\n /** Error message if silent login failed */\n error?: string;\n /** Detailed error description from OAuth provider */\n errorDescription?: string;\n}\n\n/**\n * Builds an authorization URL for silent authentication.\n *\n * This function creates an authorization URL with prompt=none,\n * which tells the OAuth provider to authenticate without user interaction.\n * If the user doesn't have an active session, the provider will return an error.\n *\n * @param config - OAuth configuration\n * @param options - Silent login options\n * @returns Authorization URL for silent authentication\n *\n * @example\n * ```ts\n * const silentAuthUrl = await buildSilentLoginUrl(oauthConfig, {\n * loginHint: 'user@example.com',\n * maxAge: 3600, // Require authentication within last hour\n * });\n *\n * // Use this URL in an iframe or hidden window\n * ```\n */\nexport async function buildSilentLoginUrl(\n config: OAuthConfig,\n options: SilentLoginOptions = {}\n): Promise<URL> {\n logger.debug({ issuer: config.issuer }, 'Building silent login URL');\n\n try {\n const server = await openid.discovery(\n new URL(config.issuer),\n config.clientId,\n config.clientSecret,\n openid.ClientSecretPost(config.clientSecret)\n );\n\n const parameters: Record<string, string> = {\n redirect_uri: config.redirect_uri,\n scope: config.scope,\n prompt: 'none', // Critical: tells provider to not show any UI\n response_type: 'code',\n ...options.additionalParams,\n };\n\n // Add optional parameters\n if (options.loginHint) {\n parameters.login_hint = options.loginHint;\n }\n\n if (options.maxAge !== undefined) {\n parameters.max_age = options.maxAge.toString();\n }\n\n const authUrl = openid.buildAuthorizationUrl(server, parameters);\n\n logger.info('Silent login URL built successfully');\n return authUrl;\n } catch (error) {\n logger.error({ error, issuer: config.issuer }, 'Failed to build silent login URL');\n throw error;\n }\n}\n\n/**\n * Checks if silent login is possible by examining the callback URL.\n *\n * After redirecting to the silent login URL, the OAuth provider will redirect back\n * with either a code (success) or an error (failure). This function parses the\n * callback URL to determine the result.\n *\n * @param callbackUrl - The URL the OAuth provider redirected to\n * @returns Result indicating success or failure with details\n *\n * @example\n * ```ts\n * // In your OAuth callback handler\n * const result = checkSilentLoginResult(new URL(request.url));\n *\n * if (result.success) {\n * // User has active SSO session, proceed with token exchange\n * const code = new URL(request.url).searchParams.get('code');\n * // ... exchange code for tokens\n * } else if (result.error === 'login_required') {\n * // User needs to log in interactively\n * redirect('/login');\n * } else {\n * // Other error\n * console.error(result.errorDescription);\n * }\n * ```\n */\nexport function checkSilentLoginResult(callbackUrl: URL): SilentLoginResult {\n const error = callbackUrl.searchParams.get('error');\n const errorDescription = callbackUrl.searchParams.get('error_description');\n const code = callbackUrl.searchParams.get('code');\n\n if (error) {\n logger.info({ error, errorDescription }, 'Silent login failed with error');\n\n return {\n success: false,\n error,\n errorDescription: errorDescription ?? undefined,\n };\n }\n\n if (code) {\n logger.info('Silent login successful');\n return {\n success: true,\n };\n }\n\n logger.warn('Silent login callback missing both code and error');\n return {\n success: false,\n error: 'invalid_callback',\n errorDescription: 'Callback URL missing both code and error parameters',\n };\n}\n\n/**\n * Common OAuth errors returned from silent login attempts.\n *\n * These can be used to handle specific error cases:\n * - login_required: User must log in interactively\n * - interaction_required: User interaction is needed (e.g., consent)\n * - consent_required: User must grant consent\n * - account_selection_required: User must select an account\n */\nexport const SilentLoginErrors = {\n LOGIN_REQUIRED: 'login_required',\n INTERACTION_REQUIRED: 'interaction_required',\n CONSENT_REQUIRED: 'consent_required',\n ACCOUNT_SELECTION_REQUIRED: 'account_selection_required',\n} as const;\n\nexport type SilentLoginError = typeof SilentLoginErrors[keyof typeof SilentLoginErrors];\n\n/**\n * Helper to check if an error requires interactive login.\n *\n * @param error - The error code from the OAuth provider\n * @returns True if the user must log in interactively\n *\n * @example\n * ```ts\n * const result = checkSilentLoginResult(callbackUrl);\n * if (!result.success && requiresInteractiveLogin(result.error)) {\n * // Redirect to full login page\n * redirect('/login');\n * }\n * ```\n */\nexport function requiresInteractiveLogin(error: string | undefined): boolean {\n if (!error) return false;\n\n return Object.values(SilentLoginErrors).includes(error as SilentLoginError);\n}\n"],"mappings":";;;;;;;;;;;;AAeA,IAAM,SAAS,aAAa,EAAE,WAAW,2BAA2B,CAAC;;;;;;;;;;;;;;;;;;;;;;AA2CrE,eAAsB,oBACpB,QACA,UAA8B,EAAE,EAClB;AACd,QAAO,MAAM,EAAE,QAAQ,OAAO,QAAQ,EAAE,4BAA4B;AAEpE,KAAI;EACF,MAAM,SAAS,MAAM,OAAO,UAC1B,IAAI,IAAI,OAAO,OAAO,EACtB,OAAO,UACP,OAAO,cACP,OAAO,iBAAiB,OAAO,aAAa,CAC7C;EAED,MAAM,aAAqC;GACzC,cAAc,OAAO;GACrB,OAAO,OAAO;GACd,QAAQ;GACR,eAAe;GACf,GAAG,QAAQ;GACZ;AAGD,MAAI,QAAQ,UACV,YAAW,aAAa,QAAQ;AAGlC,MAAI,QAAQ,WAAW,KAAA,EACrB,YAAW,UAAU,QAAQ,OAAO,UAAU;EAGhD,MAAM,UAAU,OAAO,sBAAsB,QAAQ,WAAW;AAEhE,SAAO,KAAK,sCAAsC;AAClD,SAAO;UACA,OAAO;AACd,SAAO,MAAM;GAAE;GAAO,QAAQ,OAAO;GAAQ,EAAE,mCAAmC;AAClF,QAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCV,SAAgB,uBAAuB,aAAqC;CAC1E,MAAM,QAAQ,YAAY,aAAa,IAAI,QAAQ;CACnD,MAAM,mBAAmB,YAAY,aAAa,IAAI,oBAAoB;CAC1E,MAAM,OAAO,YAAY,aAAa,IAAI,OAAO;AAEjD,KAAI,OAAO;AACT,SAAO,KAAK;GAAE;GAAO;GAAkB,EAAE,iCAAiC;AAE1E,SAAO;GACL,SAAS;GACT;GACA,kBAAkB,oBAAoB,KAAA;GACvC;;AAGH,KAAI,MAAM;AACR,SAAO,KAAK,0BAA0B;AACtC,SAAO,EACL,SAAS,MACV;;AAGH,QAAO,KAAK,oDAAoD;AAChE,QAAO;EACL,SAAS;EACT,OAAO;EACP,kBAAkB;EACnB;;;;;;;;;;;AAYH,IAAa,oBAAoB;CAC/B,gBAAgB;CAChB,sBAAsB;CACtB,kBAAkB;CAClB,4BAA4B;CAC7B;;;;;;;;;;;;;;;;AAmBD,SAAgB,yBAAyB,OAAoC;AAC3E,KAAI,CAAC,MAAO,QAAO;AAEnB,QAAO,OAAO,OAAO,kBAAkB,CAAC,SAAS,MAA0B"}
|
package/dist/types.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface Session<TData = Record<string, unknown>> {
|
|
|
13
13
|
email: string;
|
|
14
14
|
roles?: string[];
|
|
15
15
|
};
|
|
16
|
+
/** OAuth scopes granted to this session (e.g. ["openid", "profile", "email"]). */
|
|
17
|
+
scopes?: string[];
|
|
16
18
|
/** Application-specific data stored alongside the session. */
|
|
17
19
|
data?: TData;
|
|
18
20
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,MAAM,WAAW,MAAM;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB,CAAC,EAAE,IAAI,CAAC;CAC9B;AAED,mFAAmF;AACnF,MAAM,WAAW,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IACF,8DAA8D;IAC9D,IAAI,CAAC,EAAE,KAAK,CAAC;CACd;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,MAAM,WAAW,MAAM;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB,CAAC,EAAE,IAAI,CAAC;CAC9B;AAED,mFAAmF;AACnF,MAAM,WAAW,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IACF,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,KAAK,CAAC;CACd;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Session } from './types';
|
|
1
2
|
/**
|
|
2
3
|
* Converts a hex string to a Uint8Array.
|
|
3
4
|
*
|
|
@@ -80,4 +81,11 @@ export declare function generateToken(tokenLength?: number): string;
|
|
|
80
81
|
* @returns True if the access token expires within the threshold; otherwise, false.
|
|
81
82
|
*/
|
|
82
83
|
export declare const accessTokenExpires: (accessToken: string, seconds?: number) => boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Returns true if the session was granted the given scope.
|
|
86
|
+
*
|
|
87
|
+
* @param session - The session to check
|
|
88
|
+
* @param scope - The scope string to look for (e.g. "operations.read")
|
|
89
|
+
*/
|
|
90
|
+
export declare function hasScope(session: Session, scope: string): boolean;
|
|
83
91
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKvC;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAYvD;AAaD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GAAG,UAAU,GAC1B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAGD;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkCxF;AAED;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA4CxF;AAcD;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAIvE;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAIvE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,UAAU,CAKvD;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAI/C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,WAAW,GAAE,MAAW,GAAG,MAAM,CAI9D;AAGD;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAC7B,aAAa,MAAM,EACnB,UAAS,MAAW,KACnB,OAsBF,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjE"}
|
package/dist/utils.js
CHANGED
|
@@ -1,2 +1,196 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { createLogger } from "./logger.js";
|
|
2
|
+
import * as jose from "jose";
|
|
3
|
+
//#region src/utils.ts
|
|
4
|
+
var logger = createLogger({ namespace: "fides-auth:utils" });
|
|
5
|
+
/**
|
|
6
|
+
* Converts a hex string to a Uint8Array.
|
|
7
|
+
*
|
|
8
|
+
* @param hex - A hexadecimal string.
|
|
9
|
+
* @returns A Uint8Array representing the bytes of the hex string.
|
|
10
|
+
*/
|
|
11
|
+
function hexToUint8Array(hex) {
|
|
12
|
+
if (hex.length % 2 !== 0) throw new Error("Invalid hex string: odd length");
|
|
13
|
+
if (hex.length > 0 && !/^[0-9a-fA-F]+$/.test(hex)) throw new Error("Invalid hex string: contains non-hex characters");
|
|
14
|
+
const arr = new Uint8Array(hex.length / 2);
|
|
15
|
+
for (let i = 0; i < arr.length; i++) arr[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
16
|
+
return arr;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Normalizes a secret to a Uint8Array.
|
|
20
|
+
* Accepts either a hex-encoded string or an existing Uint8Array.
|
|
21
|
+
*/
|
|
22
|
+
function normalizeSecret(secret) {
|
|
23
|
+
if (typeof secret === "string") return hexToUint8Array(secret);
|
|
24
|
+
return secret;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Encrypts a payload into a JWT using the specified secret.
|
|
28
|
+
*
|
|
29
|
+
* @param payload - The payload to encrypt (can be any JSON-serializable value)
|
|
30
|
+
* @param secret - The encryption key as a hex string or Uint8Array (32 bytes for A256GCM)
|
|
31
|
+
* @returns A promise that resolves to the encrypted JWT as a string.
|
|
32
|
+
*/
|
|
33
|
+
async function createEncryptedJWT(payload, secret) {
|
|
34
|
+
return new jose.EncryptJWT(payload).setProtectedHeader({
|
|
35
|
+
alg: "dir",
|
|
36
|
+
enc: "A256GCM"
|
|
37
|
+
}).setIssuedAt().encrypt(normalizeSecret(secret));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Encrypts the given text using AES-GCM via the Web Crypto API.
|
|
41
|
+
* Returns a string in the format: iv:authTag:ciphertext, all in hex.
|
|
42
|
+
*
|
|
43
|
+
* @param text - The plaintext to encrypt.
|
|
44
|
+
* @param secret - The encryption key as a hex string or Uint8Array (32 bytes for AES-256).
|
|
45
|
+
* @returns A promise that resolves to the encrypted string.
|
|
46
|
+
*/
|
|
47
|
+
async function encrypt(text, secret) {
|
|
48
|
+
const keyData = normalizeSecret(secret);
|
|
49
|
+
const key = await crypto.subtle.importKey("raw", keyData, { name: "AES-GCM" }, false, ["encrypt"]);
|
|
50
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
51
|
+
const data = new TextEncoder().encode(text);
|
|
52
|
+
const encryptedBuffer = await crypto.subtle.encrypt({
|
|
53
|
+
name: "AES-GCM",
|
|
54
|
+
iv,
|
|
55
|
+
tagLength: 128
|
|
56
|
+
}, key, data);
|
|
57
|
+
const encryptedArray = new Uint8Array(encryptedBuffer);
|
|
58
|
+
const tagLength = 16;
|
|
59
|
+
const ciphertext = encryptedArray.slice(0, encryptedArray.length - tagLength);
|
|
60
|
+
const authTag = encryptedArray.slice(encryptedArray.length - tagLength);
|
|
61
|
+
return [
|
|
62
|
+
toHex(iv),
|
|
63
|
+
toHex(authTag),
|
|
64
|
+
toHex(ciphertext)
|
|
65
|
+
].join(":");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Decrypts data encrypted with AES-256-GCM (using the Web Crypto API).
|
|
69
|
+
* The input is expected to be in the format: iv:authTag:ciphertext (all hex-encoded).
|
|
70
|
+
*
|
|
71
|
+
* @param data - The encrypted data string.
|
|
72
|
+
* @param secret - The decryption key as a hex string or Uint8Array (32 bytes for AES-256).
|
|
73
|
+
* @returns A promise that resolves to the decrypted plaintext.
|
|
74
|
+
*/
|
|
75
|
+
async function decrypt(data, secret) {
|
|
76
|
+
const [ivHex, tagHex, ciphertextHex] = data.split(":");
|
|
77
|
+
if (!ivHex || !tagHex || !ciphertextHex) throw new Error("Invalid encrypted data format");
|
|
78
|
+
const iv = hexToUint8Array(ivHex);
|
|
79
|
+
const tag = hexToUint8Array(tagHex);
|
|
80
|
+
const ciphertext = hexToUint8Array(ciphertextHex);
|
|
81
|
+
const combined = new Uint8Array(ciphertext.length + tag.length);
|
|
82
|
+
combined.set(ciphertext);
|
|
83
|
+
combined.set(tag, ciphertext.length);
|
|
84
|
+
const keyData = normalizeSecret(secret);
|
|
85
|
+
const keyDataArrayBuffer = new Uint8Array([...keyData]);
|
|
86
|
+
const ivArrayBuffer = new Uint8Array([...iv]);
|
|
87
|
+
const key = await crypto.subtle.importKey("raw", keyDataArrayBuffer, { name: "AES-GCM" }, false, ["decrypt"]);
|
|
88
|
+
const decryptedBuffer = await crypto.subtle.decrypt({
|
|
89
|
+
name: "AES-GCM",
|
|
90
|
+
iv: ivArrayBuffer,
|
|
91
|
+
tagLength: 128
|
|
92
|
+
}, key, combined.buffer);
|
|
93
|
+
return new TextDecoder().decode(decryptedBuffer);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Converts input data (string or Uint8Array) to a Uint8Array backed by a plain ArrayBuffer.
|
|
97
|
+
* @param data - The input data.
|
|
98
|
+
* @returns A Uint8Array representing the input data.
|
|
99
|
+
*/
|
|
100
|
+
function toUint8Array(data) {
|
|
101
|
+
if (typeof data === "string") return new TextEncoder().encode(data);
|
|
102
|
+
return data;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Hashes the input using SHA-256 with the Web Crypto API.
|
|
106
|
+
* @param data - The input data (string or Uint8Array).
|
|
107
|
+
* @returns A Promise that resolves to the SHA-256 hash as a hex string.
|
|
108
|
+
*/
|
|
109
|
+
async function sha256(data) {
|
|
110
|
+
const buffer = toUint8Array(data);
|
|
111
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
|
|
112
|
+
return toHex(new Uint8Array(hashBuffer));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Hashes the input using SHA-512 with the Web Crypto API.
|
|
116
|
+
* @param data - The input data (string or Uint8Array).
|
|
117
|
+
* @returns A Promise that resolves to the SHA-512 hash as a hex string.
|
|
118
|
+
*/
|
|
119
|
+
async function sha512(data) {
|
|
120
|
+
const buffer = toUint8Array(data);
|
|
121
|
+
const hashBuffer = await crypto.subtle.digest("SHA-512", buffer);
|
|
122
|
+
return toHex(new Uint8Array(hashBuffer));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Retrieves the session secret from environment variables.
|
|
126
|
+
* Throws an error if the SESSION_SECRET is not defined.
|
|
127
|
+
*
|
|
128
|
+
* @returns {string} The session secret.
|
|
129
|
+
*/
|
|
130
|
+
function getSessionSecret() {
|
|
131
|
+
const secret = process.env.SESSION_SECRET;
|
|
132
|
+
if (!secret) throw new Error("SESSION_SECRET is not defined");
|
|
133
|
+
return secret;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Retrieves the session secret from environment variables as Uint8Array .
|
|
137
|
+
* Throws an error if the SESSION_SECRET is not defined.
|
|
138
|
+
*
|
|
139
|
+
* @returns {Uint8Array} The session secret as Uint8Array.
|
|
140
|
+
*/
|
|
141
|
+
function getSessionSecretUint8Array() {
|
|
142
|
+
const keyData = hexToUint8Array(getSessionSecret());
|
|
143
|
+
return new Uint8Array([...keyData]);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Converts a Uint8Array to a lowercase hexadecimal string.
|
|
147
|
+
*/
|
|
148
|
+
function toHex(bytes) {
|
|
149
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Generates a random token of the given byte length and returns it as a hex string.
|
|
153
|
+
* Default token length is 20 bytes (160 bits).
|
|
154
|
+
*
|
|
155
|
+
* @param tokenLength - The number of random bytes to generate (default is 20).
|
|
156
|
+
* @returns A hex string representing the token.
|
|
157
|
+
*/
|
|
158
|
+
function generateToken(tokenLength = 32) {
|
|
159
|
+
const bytes = new Uint8Array(tokenLength);
|
|
160
|
+
crypto.getRandomValues(bytes);
|
|
161
|
+
return toHex(bytes);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Determines if the access token of a session will expire in less than the given threshold.
|
|
165
|
+
*
|
|
166
|
+
* It decodes the access token (assumed to be a JWT) and reads the `exp` claim.
|
|
167
|
+
* The expiration (`exp`) claim is the UNIX timestamp (in seconds) when the token expires.
|
|
168
|
+
*
|
|
169
|
+
* @param accessToken - The access token string.
|
|
170
|
+
* @param seconds - The threshold (in seconds) for checking expiration (default is 10 seconds).
|
|
171
|
+
* @returns True if the access token expires within the threshold; otherwise, false.
|
|
172
|
+
*/
|
|
173
|
+
var accessTokenExpires = (accessToken, seconds = 10) => {
|
|
174
|
+
try {
|
|
175
|
+
const payload = jose.decodeJwt(accessToken);
|
|
176
|
+
if (!payload.exp) return false;
|
|
177
|
+
const nowInSeconds = Date.now() / 1e3;
|
|
178
|
+
return payload.exp - nowInSeconds < seconds;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logger.error({ error }, "Error decoding access token");
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Returns true if the session was granted the given scope.
|
|
186
|
+
*
|
|
187
|
+
* @param session - The session to check
|
|
188
|
+
* @param scope - The scope string to look for (e.g. "operations.read")
|
|
189
|
+
*/
|
|
190
|
+
function hasScope(session, scope) {
|
|
191
|
+
return session.scopes?.includes(scope) ?? false;
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
export { accessTokenExpires, createEncryptedJWT, decrypt, encrypt, generateToken, getSessionSecret, getSessionSecretUint8Array, hasScope, hexToUint8Array, sha256, sha512, toHex };
|
|
195
|
+
|
|
196
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import * as jose from 'jose';\n\nimport { createLogger } from './logger';\nimport type { Session } from './types';\n\nconst logger = createLogger({ namespace: 'fides-auth:utils' });\n\n\n/**\n * Converts a hex string to a Uint8Array.\n *\n * @param hex - A hexadecimal string.\n * @returns A Uint8Array representing the bytes of the hex string.\n */\nexport function hexToUint8Array(hex: string): Uint8Array {\n if (hex.length % 2 !== 0) {\n throw new Error('Invalid hex string: odd length');\n }\n if (hex.length > 0 && !/^[0-9a-fA-F]+$/.test(hex)) {\n throw new Error('Invalid hex string: contains non-hex characters');\n }\n const arr = new Uint8Array(hex.length / 2);\n for (let i = 0; i < arr.length; i++) {\n arr[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return arr;\n}\n\n/**\n * Normalizes a secret to a Uint8Array.\n * Accepts either a hex-encoded string or an existing Uint8Array.\n */\nfunction normalizeSecret(secret: string | Uint8Array): Uint8Array {\n if (typeof secret === 'string') {\n return hexToUint8Array(secret);\n }\n return secret;\n}\n\n/**\n * Encrypts a payload into a JWT using the specified secret.\n *\n * @param payload - The payload to encrypt (can be any JSON-serializable value)\n * @param secret - The encryption key as a hex string or Uint8Array (32 bytes for A256GCM)\n * @returns A promise that resolves to the encrypted JWT as a string.\n */\nexport async function createEncryptedJWT(\n payload: unknown,\n secret: string | Uint8Array,\n): Promise<string> {\n return new jose.EncryptJWT(payload as jose.JWTPayload)\n .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })\n .setIssuedAt()\n .encrypt(normalizeSecret(secret));\n}\n\n\n/**\n * Encrypts the given text using AES-GCM via the Web Crypto API.\n * Returns a string in the format: iv:authTag:ciphertext, all in hex.\n *\n * @param text - The plaintext to encrypt.\n * @param secret - The encryption key as a hex string or Uint8Array (32 bytes for AES-256).\n * @returns A promise that resolves to the encrypted string.\n */\nexport async function encrypt(text: string, secret: string | Uint8Array): Promise<string> {\n const keyData = normalizeSecret(secret);\n\n // Import the key for AES-GCM encryption.\n const key = await crypto.subtle.importKey(\n 'raw',\n keyData as BufferSource,\n { name: 'AES-GCM' },\n false,\n ['encrypt']\n );\n\n // Generate a 12-byte IV using the Web Crypto API.\n const iv = crypto.getRandomValues(new Uint8Array(12));\n\n // Encode the plaintext into a Uint8Array.\n const encoder = new TextEncoder();\n const data = encoder.encode(text);\n\n // Encrypt the data using AES-GCM.\n // Note: The Web Crypto API combines the ciphertext and authentication tag in the output.\n const encryptedBuffer = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv, tagLength: 128 },\n key,\n data\n );\n\n // Convert the encrypted data to a Uint8Array for separation.\n const encryptedArray = new Uint8Array(encryptedBuffer);\n const tagLength = 16; // AES-GCM uses a 128-bit (16-byte) tag.\n const ciphertext = encryptedArray.slice(0, encryptedArray.length - tagLength);\n const authTag = encryptedArray.slice(encryptedArray.length - tagLength);\n\n return [toHex(iv), toHex(authTag), toHex(ciphertext)].join(':');\n}\n\n/**\n * Decrypts data encrypted with AES-256-GCM (using the Web Crypto API).\n * The input is expected to be in the format: iv:authTag:ciphertext (all hex-encoded).\n *\n * @param data - The encrypted data string.\n * @param secret - The decryption key as a hex string or Uint8Array (32 bytes for AES-256).\n * @returns A promise that resolves to the decrypted plaintext.\n */\nexport async function decrypt(data: string, secret: string | Uint8Array): Promise<string> {\n\n // Split the incoming data. We expect exactly three parts.\n const [ivHex, tagHex, ciphertextHex] = data.split(':');\n if (!ivHex || !tagHex || !ciphertextHex) {\n throw new Error('Invalid encrypted data format');\n }\n\n // Convert each hex-encoded part to a Uint8Array\n const iv = hexToUint8Array(ivHex);\n const tag = hexToUint8Array(tagHex);\n const ciphertext = hexToUint8Array(ciphertextHex);\n\n // Concatenate ciphertext and auth tag (Web Crypto expects them combined)\n const combined = new Uint8Array(ciphertext.length + tag.length);\n combined.set(ciphertext);\n combined.set(tag, ciphertext.length);\n\n // Convert the secret and import it as a CryptoKey for AES-GCM decryption.\n const keyData = normalizeSecret(secret);\n // Ensure keyData is backed by a real ArrayBuffer (not SharedArrayBuffer)\n const keyDataArrayBuffer = new Uint8Array([...keyData]);\n\n // Ensure iv is backed by a real ArrayBuffer (not SharedArrayBuffer)\n const ivArrayBuffer = new Uint8Array([...iv]);\n\n const key = await crypto.subtle.importKey(\n 'raw',\n keyDataArrayBuffer as BufferSource,\n { name: 'AES-GCM' },\n false,\n ['decrypt']\n );\n\n // Decrypt the combined ciphertext+tag using the IV.\n const decryptedBuffer = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: ivArrayBuffer, tagLength: 128 },\n key,\n combined.buffer\n );\n\n // Decode the decrypted ArrayBuffer back to a string.\n const decoder = new TextDecoder();\n return decoder.decode(decryptedBuffer);\n}\n\n/**\n * Converts input data (string or Uint8Array) to a Uint8Array backed by a plain ArrayBuffer.\n * @param data - The input data.\n * @returns A Uint8Array representing the input data.\n */\nfunction toUint8Array(data: string | Uint8Array): Uint8Array {\n if (typeof data === 'string') {\n return new TextEncoder().encode(data);\n }\n return data;\n}\n\n/**\n * Hashes the input using SHA-256 with the Web Crypto API.\n * @param data - The input data (string or Uint8Array).\n * @returns A Promise that resolves to the SHA-256 hash as a hex string.\n */\nexport async function sha256(data: string | Uint8Array): Promise<string> {\n const buffer = toUint8Array(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer as BufferSource);\n return toHex(new Uint8Array(hashBuffer));\n}\n\n/**\n * Hashes the input using SHA-512 with the Web Crypto API.\n * @param data - The input data (string or Uint8Array).\n * @returns A Promise that resolves to the SHA-512 hash as a hex string.\n */\nexport async function sha512(data: string | Uint8Array): Promise<string> {\n const buffer = toUint8Array(data);\n const hashBuffer = await crypto.subtle.digest('SHA-512', buffer as BufferSource);\n return toHex(new Uint8Array(hashBuffer));\n}\n\n/**\n * Retrieves the session secret from environment variables.\n * Throws an error if the SESSION_SECRET is not defined.\n *\n * @returns {string} The session secret.\n */\nexport function getSessionSecret(): string {\n const secret = process.env.SESSION_SECRET;\n if (!secret) {\n throw new Error('SESSION_SECRET is not defined');\n }\n return secret;\n}\n\n/**\n * Retrieves the session secret from environment variables as Uint8Array .\n * Throws an error if the SESSION_SECRET is not defined.\n *\n * @returns {Uint8Array} The session secret as Uint8Array.\n */\nexport function getSessionSecretUint8Array(): Uint8Array {\n const secret = getSessionSecret();\n const keyData = hexToUint8Array(secret);\n // Ensure it's backed by a regular ArrayBuffer (not SharedArrayBuffer)\n return new Uint8Array([...keyData]);\n}\n\n/**\n * Converts a Uint8Array to a lowercase hexadecimal string.\n */\nexport function toHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Generates a random token of the given byte length and returns it as a hex string.\n * Default token length is 20 bytes (160 bits).\n *\n * @param tokenLength - The number of random bytes to generate (default is 20).\n * @returns A hex string representing the token.\n */\nexport function generateToken(tokenLength: number = 32): string {\n const bytes = new Uint8Array(tokenLength);\n crypto.getRandomValues(bytes);\n return toHex(bytes);\n}\n\n\n/**\n * Determines if the access token of a session will expire in less than the given threshold.\n *\n * It decodes the access token (assumed to be a JWT) and reads the `exp` claim.\n * The expiration (`exp`) claim is the UNIX timestamp (in seconds) when the token expires.\n *\n * @param accessToken - The access token string.\n * @param seconds - The threshold (in seconds) for checking expiration (default is 10 seconds).\n * @returns True if the access token expires within the threshold; otherwise, false.\n */\nexport const accessTokenExpires = (\n accessToken: string,\n seconds: number = 10\n): boolean => {\n try {\n // Decode the JWT without verifying the signature.\n const payload = jose.decodeJwt(accessToken) as { exp?: number; };\n\n // If there is no exp claim, assume the token does not expire\n if (!payload.exp) {\n return false;\n }\n\n const nowInSeconds = Date.now() / 1000;\n const remainingSeconds = payload.exp - nowInSeconds;\n\n return remainingSeconds < seconds;\n } catch (error) {\n // If decoding fails, treat the token as invalid so no refresh is triggered.\n logger.error(\n { error },\n 'Error decoding access token'\n );\n return true;\n }\n};\n\n/**\n * Returns true if the session was granted the given scope.\n *\n * @param session - The session to check\n * @param scope - The scope string to look for (e.g. \"operations.read\")\n */\nexport function hasScope(session: Session, scope: string): boolean {\n return session.scopes?.includes(scope) ?? false;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventuras/fides-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Framework-agnostic OAuth/OIDC authentication library with PKCE, session management, and pluggable logging",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"oauth",
|
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
"types": "./dist/oauth.d.ts",
|
|
48
48
|
"import": "./dist/oauth.js"
|
|
49
49
|
},
|
|
50
|
+
"./oauth-logging": {
|
|
51
|
+
"types": "./dist/oauth-logging.d.ts",
|
|
52
|
+
"import": "./dist/oauth-logging.js"
|
|
53
|
+
},
|
|
50
54
|
"./oauth-browser": {
|
|
51
55
|
"types": "./dist/oauth-browser.d.ts",
|
|
52
56
|
"import": "./dist/oauth-browser.js"
|
|
@@ -88,7 +92,7 @@
|
|
|
88
92
|
"vite": "^8.0.8",
|
|
89
93
|
"vitest": "^4.1.4",
|
|
90
94
|
"@eventuras/typescript-config": "1.0.0",
|
|
91
|
-
"@eventuras/vite-config": "0.2.
|
|
95
|
+
"@eventuras/vite-config": "0.2.2"
|
|
92
96
|
},
|
|
93
97
|
"engines": {
|
|
94
98
|
"node": ">=18"
|