@harperfast/oauth 1.2.1
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 +201 -0
- package/README.md +219 -0
- package/assets/test.html +321 -0
- package/config.yaml +23 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +241 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/CSRFTokenManager.d.ts +32 -0
- package/dist/lib/CSRFTokenManager.js +90 -0
- package/dist/lib/CSRFTokenManager.js.map +1 -0
- package/dist/lib/OAuthProvider.d.ts +59 -0
- package/dist/lib/OAuthProvider.js +370 -0
- package/dist/lib/OAuthProvider.js.map +1 -0
- package/dist/lib/config.d.ts +31 -0
- package/dist/lib/config.js +138 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/handlers.d.ts +56 -0
- package/dist/lib/handlers.js +386 -0
- package/dist/lib/handlers.js.map +1 -0
- package/dist/lib/hookManager.d.ts +52 -0
- package/dist/lib/hookManager.js +114 -0
- package/dist/lib/hookManager.js.map +1 -0
- package/dist/lib/providers/auth0.d.ts +8 -0
- package/dist/lib/providers/auth0.js +34 -0
- package/dist/lib/providers/auth0.js.map +1 -0
- package/dist/lib/providers/azure.d.ts +7 -0
- package/dist/lib/providers/azure.js +33 -0
- package/dist/lib/providers/azure.js.map +1 -0
- package/dist/lib/providers/generic.d.ts +7 -0
- package/dist/lib/providers/generic.js +20 -0
- package/dist/lib/providers/generic.js.map +1 -0
- package/dist/lib/providers/github.d.ts +7 -0
- package/dist/lib/providers/github.js +73 -0
- package/dist/lib/providers/github.js.map +1 -0
- package/dist/lib/providers/google.d.ts +7 -0
- package/dist/lib/providers/google.js +27 -0
- package/dist/lib/providers/google.js.map +1 -0
- package/dist/lib/providers/index.d.ts +17 -0
- package/dist/lib/providers/index.js +49 -0
- package/dist/lib/providers/index.js.map +1 -0
- package/dist/lib/providers/okta.d.ts +8 -0
- package/dist/lib/providers/okta.js +45 -0
- package/dist/lib/providers/okta.js.map +1 -0
- package/dist/lib/providers/validation.d.ts +67 -0
- package/dist/lib/providers/validation.js +156 -0
- package/dist/lib/providers/validation.js.map +1 -0
- package/dist/lib/resource.d.ts +102 -0
- package/dist/lib/resource.js +368 -0
- package/dist/lib/resource.js.map +1 -0
- package/dist/lib/sessionValidator.d.ts +38 -0
- package/dist/lib/sessionValidator.js +162 -0
- package/dist/lib/sessionValidator.js.map +1 -0
- package/dist/lib/tenantManager.d.ts +102 -0
- package/dist/lib/tenantManager.js +177 -0
- package/dist/lib/tenantManager.js.map +1 -0
- package/dist/lib/withOAuthValidation.d.ts +64 -0
- package/dist/lib/withOAuthValidation.js +188 -0
- package/dist/lib/withOAuthValidation.js.map +1 -0
- package/dist/types.d.ts +326 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +89 -0
- package/schema/oauth.graphql +21 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSRF Token Manager for OAuth flows
|
|
3
|
+
*
|
|
4
|
+
* Manages CSRF protection tokens that prevent cross-site request forgery
|
|
5
|
+
* during OAuth authorization flows. Tokens are stored in Harper table
|
|
6
|
+
* for distributed access across workers and cluster nodes.
|
|
7
|
+
*/
|
|
8
|
+
// Lazy-load the CSRF token table
|
|
9
|
+
let csrfTable;
|
|
10
|
+
function getCSRFTable() {
|
|
11
|
+
if (!csrfTable) {
|
|
12
|
+
// Check if oauth database and table exist
|
|
13
|
+
if (!databases?.oauth?.csrf_tokens) {
|
|
14
|
+
throw new Error('OAuth CSRF tokens table (oauth.csrf_tokens) not found. ' +
|
|
15
|
+
'Please ensure the OAuth plugin is properly installed with its schema.');
|
|
16
|
+
}
|
|
17
|
+
csrfTable = databases.oauth.csrf_tokens;
|
|
18
|
+
}
|
|
19
|
+
return csrfTable;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reset the cached CSRF table reference (for testing only)
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export function resetCSRFTableCache() {
|
|
26
|
+
csrfTable = undefined;
|
|
27
|
+
}
|
|
28
|
+
export class CSRFTokenManager {
|
|
29
|
+
logger;
|
|
30
|
+
constructor(logger) {
|
|
31
|
+
this.logger = logger;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Store CSRF token with metadata
|
|
35
|
+
* Table expiration is handled by Harper (10 minutes)
|
|
36
|
+
*/
|
|
37
|
+
async set(token, data) {
|
|
38
|
+
const table = getCSRFTable();
|
|
39
|
+
try {
|
|
40
|
+
await table.put({
|
|
41
|
+
token_id: token,
|
|
42
|
+
data: JSON.stringify(data),
|
|
43
|
+
created_at: Date.now(),
|
|
44
|
+
});
|
|
45
|
+
this.logger?.debug?.(`Stored CSRF token: ${token}`);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
this.logger?.error?.('Failed to store CSRF token:', error);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retrieve CSRF token
|
|
54
|
+
* Harper automatically handles expiration via table-level setting
|
|
55
|
+
*/
|
|
56
|
+
async get(token) {
|
|
57
|
+
const table = getCSRFTable();
|
|
58
|
+
try {
|
|
59
|
+
const record = await table.get(token);
|
|
60
|
+
if (!record || !record.data) {
|
|
61
|
+
this.logger?.debug?.(`CSRF token not found: ${token}`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const data = JSON.parse(record.data);
|
|
65
|
+
this.logger?.debug?.(`Retrieved CSRF token: ${token}`);
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
this.logger?.error?.('Failed to retrieve CSRF token:', error);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Delete CSRF token (after successful verification)
|
|
75
|
+
*/
|
|
76
|
+
async delete(token) {
|
|
77
|
+
const table = getCSRFTable();
|
|
78
|
+
try {
|
|
79
|
+
await table.delete(token);
|
|
80
|
+
this.logger?.debug?.(`Deleted CSRF token: ${token}`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
// Not critical if delete fails (expiration will clean it up)
|
|
84
|
+
this.logger?.warn?.('Failed to delete CSRF token:', error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Singleton instance that can be shared across providers
|
|
89
|
+
export const csrfTokenManager = new CSRFTokenManager();
|
|
90
|
+
//# sourceMappingURL=CSRFTokenManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CSRFTokenManager.js","sourceRoot":"","sources":["../../src/lib/CSRFTokenManager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,iCAAiC;AACjC,IAAI,SAAgB,CAAC;AAErB,SAAS,YAAY;IACpB,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,0CAA0C;QAC1C,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACd,yDAAyD;gBACxD,uEAAuE,CACxE,CAAC;QACH,CAAC;QACD,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC;IACzC,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IAClC,SAAS,GAAG,SAAgB,CAAC;AAC9B,CAAC;AAED,MAAM,OAAO,gBAAgB;IACpB,MAAM,CAAU;IAExB,YAAY,MAAe;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAC,KAAa,EAAE,IAAmB;QAC3C,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;QAE7B,IAAI,CAAC;YACJ,MAAM,KAAK,CAAC,GAAG,CAAC;gBACf,QAAQ,EAAE,KAAK;gBACf,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC1B,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,sBAAsB,KAAK,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;YAC3D,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CAAC,KAAa;QACtB,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;QAE7B,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAEtC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC7B,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;gBACvD,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;YACvD,OAAO,IAAI,CAAC;QACb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,KAAa;QACzB,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;QAE7B,IAAI,CAAC;YACJ,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,uBAAuB,KAAK,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,6DAA6D;YAC7D,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QAC5D,CAAC;IACF,CAAC;CACD;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Provider Base Class
|
|
3
|
+
*
|
|
4
|
+
* Handles OAuth 2.0 authentication flow with any compliant provider
|
|
5
|
+
*/
|
|
6
|
+
import type { OAuthProviderConfig, Logger, CSRFTokenData, TokenResponse, OAuthUser, IOAuthProvider } from '../types.ts';
|
|
7
|
+
export declare class OAuthProvider implements IOAuthProvider {
|
|
8
|
+
config: OAuthProviderConfig;
|
|
9
|
+
logger?: Logger;
|
|
10
|
+
private jwksClient?;
|
|
11
|
+
constructor(config: OAuthProviderConfig, logger?: Logger);
|
|
12
|
+
private initializeJwksClient;
|
|
13
|
+
private validateConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Generate authorization URL for OAuth login
|
|
16
|
+
*/
|
|
17
|
+
getAuthorizationUrl(state: string, redirectUri?: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Exchange authorization code for access token
|
|
20
|
+
*/
|
|
21
|
+
exchangeCodeForToken(code: string, redirectUri?: string): Promise<TokenResponse>;
|
|
22
|
+
/**
|
|
23
|
+
* Get user info using access token
|
|
24
|
+
*/
|
|
25
|
+
getUserInfo(accessToken: string, idTokenClaims?: any): Promise<any>;
|
|
26
|
+
/**
|
|
27
|
+
* Fetch user info from the provider's userinfo endpoint
|
|
28
|
+
*/
|
|
29
|
+
fetchUserInfo(accessToken: string): Promise<any>;
|
|
30
|
+
/**
|
|
31
|
+
* Verify ID token with proper signature verification using JWKS
|
|
32
|
+
*/
|
|
33
|
+
verifyIdToken(idToken: string): Promise<any>;
|
|
34
|
+
/**
|
|
35
|
+
* Verify ID token claims without signature verification
|
|
36
|
+
* Used as fallback when JWKS is not available
|
|
37
|
+
*/
|
|
38
|
+
private verifyIdTokenClaims;
|
|
39
|
+
/**
|
|
40
|
+
* Map OAuth user info to Harper user object
|
|
41
|
+
*/
|
|
42
|
+
mapUserToHarper(userInfo: any): OAuthUser;
|
|
43
|
+
/**
|
|
44
|
+
* Extract a claim from user info (supports nested paths)
|
|
45
|
+
*/
|
|
46
|
+
private extractClaim;
|
|
47
|
+
/**
|
|
48
|
+
* Generate and store CSRF token for protection
|
|
49
|
+
*/
|
|
50
|
+
generateCSRFToken(metadata?: Record<string, any>): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Verify and consume CSRF token
|
|
53
|
+
*/
|
|
54
|
+
verifyCSRFToken(token: string): Promise<CSRFTokenData | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Refresh an access token using a refresh token
|
|
57
|
+
*/
|
|
58
|
+
refreshAccessToken(refreshToken: string): Promise<TokenResponse>;
|
|
59
|
+
}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Provider Base Class
|
|
3
|
+
*
|
|
4
|
+
* Handles OAuth 2.0 authentication flow with any compliant provider
|
|
5
|
+
*/
|
|
6
|
+
import crypto from 'node:crypto';
|
|
7
|
+
import jwt from 'jsonwebtoken';
|
|
8
|
+
import jwksClient from 'jwks-rsa';
|
|
9
|
+
import { csrfTokenManager } from "./CSRFTokenManager.js";
|
|
10
|
+
export class OAuthProvider {
|
|
11
|
+
config;
|
|
12
|
+
logger;
|
|
13
|
+
jwksClient;
|
|
14
|
+
constructor(config, logger) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
// Pass logger to the singleton csrfTokenManager if not already set
|
|
18
|
+
if (!csrfTokenManager['logger']) {
|
|
19
|
+
csrfTokenManager['logger'] = logger;
|
|
20
|
+
}
|
|
21
|
+
this.validateConfig();
|
|
22
|
+
this.initializeJwksClient();
|
|
23
|
+
}
|
|
24
|
+
initializeJwksClient() {
|
|
25
|
+
// Only initialize if we have a JWKS URI
|
|
26
|
+
if (this.config.jwksUri) {
|
|
27
|
+
this.jwksClient = jwksClient({
|
|
28
|
+
jwksUri: this.config.jwksUri,
|
|
29
|
+
cache: true, // Cache keys to avoid repeated fetches
|
|
30
|
+
cacheMaxEntries: 5, // Max number of keys to cache
|
|
31
|
+
cacheMaxAge: 10 * 60 * 60 * 1000, // 10 hours
|
|
32
|
+
rateLimit: true, // Rate limit to prevent abuse
|
|
33
|
+
jwksRequestsPerMinute: 10,
|
|
34
|
+
// Timeout for JWKS fetch
|
|
35
|
+
timeout: 5000,
|
|
36
|
+
});
|
|
37
|
+
this.logger?.info?.(`JWKS client initialized for ${this.config.provider}`);
|
|
38
|
+
}
|
|
39
|
+
else if (this.config.provider !== 'generic') {
|
|
40
|
+
this.logger?.warn?.(`No JWKS URI configured for ${this.config.provider} - ID token signatures will not be verified`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
validateConfig() {
|
|
44
|
+
const required = ['clientId', 'clientSecret', 'authorizationUrl', 'tokenUrl', 'userInfoUrl'];
|
|
45
|
+
const missing = required.filter((key) => !this.config[key]);
|
|
46
|
+
if (missing.length > 0) {
|
|
47
|
+
throw new Error(`OAuth configuration missing required fields: ${missing.join(', ')}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generate authorization URL for OAuth login
|
|
52
|
+
*/
|
|
53
|
+
getAuthorizationUrl(state, redirectUri) {
|
|
54
|
+
const params = new URLSearchParams({
|
|
55
|
+
client_id: this.config.clientId,
|
|
56
|
+
redirect_uri: redirectUri || this.config.redirectUri || '',
|
|
57
|
+
response_type: 'code',
|
|
58
|
+
scope: this.config.scope || '',
|
|
59
|
+
state: state,
|
|
60
|
+
});
|
|
61
|
+
// Add provider-specific parameters
|
|
62
|
+
if (this.config.additionalParams) {
|
|
63
|
+
Object.entries(this.config.additionalParams).forEach(([key, value]) => {
|
|
64
|
+
params.set(key, value);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return `${this.config.authorizationUrl}?${params}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Exchange authorization code for access token
|
|
71
|
+
*/
|
|
72
|
+
async exchangeCodeForToken(code, redirectUri) {
|
|
73
|
+
const params = new URLSearchParams({
|
|
74
|
+
grant_type: 'authorization_code',
|
|
75
|
+
code: code,
|
|
76
|
+
redirect_uri: redirectUri || this.config.redirectUri || '',
|
|
77
|
+
client_id: this.config.clientId,
|
|
78
|
+
client_secret: this.config.clientSecret,
|
|
79
|
+
});
|
|
80
|
+
const response = await fetch(this.config.tokenUrl, {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
84
|
+
'Accept': 'application/json',
|
|
85
|
+
},
|
|
86
|
+
body: params.toString(),
|
|
87
|
+
});
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const contentType = response.headers.get('content-type');
|
|
90
|
+
if (contentType?.includes('application/json')) {
|
|
91
|
+
try {
|
|
92
|
+
const errorBody = await response.json();
|
|
93
|
+
const detail = errorBody.error_description || errorBody.error || response.statusText;
|
|
94
|
+
throw new Error(`Token exchange failed: ${detail}`);
|
|
95
|
+
}
|
|
96
|
+
catch (parseError) {
|
|
97
|
+
if (parseError instanceof Error && parseError.message.startsWith('Token exchange failed'))
|
|
98
|
+
throw parseError;
|
|
99
|
+
// JSON parse failed despite content-type header — fall through to generic error
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Drain unconsumed body to free the underlying socket (undici connection pool)
|
|
103
|
+
await response.body?.cancel();
|
|
104
|
+
throw new Error(`Token exchange failed: provider returned ${response.status} ${response.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
const contentType = response.headers.get('content-type');
|
|
107
|
+
if (contentType?.includes('application/json')) {
|
|
108
|
+
return response.json();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Some providers (like GitHub) return form-encoded data
|
|
112
|
+
const text = await response.text();
|
|
113
|
+
const tokenParams = new URLSearchParams(text);
|
|
114
|
+
return Object.fromEntries(tokenParams);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get user info using access token
|
|
119
|
+
*/
|
|
120
|
+
async getUserInfo(accessToken, idTokenClaims = null) {
|
|
121
|
+
// Check if provider has custom getUserInfo implementation
|
|
122
|
+
if (typeof this.config.getUserInfo === 'function') {
|
|
123
|
+
const helpers = {
|
|
124
|
+
getUserInfo: this.fetchUserInfo.bind(this),
|
|
125
|
+
logger: this.logger,
|
|
126
|
+
};
|
|
127
|
+
return this.config.getUserInfo.call(this, accessToken, helpers);
|
|
128
|
+
}
|
|
129
|
+
// If we have verified ID token claims and config says to prefer them
|
|
130
|
+
if (idTokenClaims && this.config.preferIdToken !== false) {
|
|
131
|
+
this.logger?.debug?.('Using verified ID token claims for user info');
|
|
132
|
+
// Some providers don't include email in ID token, fetch it separately
|
|
133
|
+
if (!idTokenClaims.email && this.config.fetchEmail) {
|
|
134
|
+
try {
|
|
135
|
+
const additionalInfo = await this.fetchUserInfo(accessToken);
|
|
136
|
+
return { ...idTokenClaims, ...additionalInfo };
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
this.logger?.warn?.('Failed to fetch additional user info:', error.message);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return idTokenClaims;
|
|
143
|
+
}
|
|
144
|
+
// Fetch from userinfo endpoint
|
|
145
|
+
return this.fetchUserInfo(accessToken);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Fetch user info from the provider's userinfo endpoint
|
|
149
|
+
*/
|
|
150
|
+
async fetchUserInfo(accessToken) {
|
|
151
|
+
const response = await fetch(this.config.userInfoUrl, {
|
|
152
|
+
headers: {
|
|
153
|
+
Authorization: `Bearer ${accessToken}`,
|
|
154
|
+
Accept: 'application/json',
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
throw new Error(`Failed to fetch user info: ${response.statusText}`);
|
|
159
|
+
}
|
|
160
|
+
return response.json();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Verify ID token with proper signature verification using JWKS
|
|
164
|
+
*/
|
|
165
|
+
async verifyIdToken(idToken) {
|
|
166
|
+
// First decode to get the header and payload
|
|
167
|
+
const decoded = jwt.decode(idToken, { complete: true });
|
|
168
|
+
if (!decoded || !decoded.payload || !decoded.header) {
|
|
169
|
+
throw new Error('Invalid ID token format');
|
|
170
|
+
}
|
|
171
|
+
// If we have a JWKS client, verify the signature
|
|
172
|
+
if (this.jwksClient) {
|
|
173
|
+
try {
|
|
174
|
+
// Get the signing key from JWKS
|
|
175
|
+
const kid = decoded.header.kid;
|
|
176
|
+
if (!kid) {
|
|
177
|
+
throw new Error('ID token missing key ID (kid) in header');
|
|
178
|
+
}
|
|
179
|
+
// This fetches the key from JWKS endpoint (with caching)
|
|
180
|
+
const key = await this.jwksClient.getSigningKey(kid);
|
|
181
|
+
const publicKey = key.getPublicKey();
|
|
182
|
+
// Verify signature and claims
|
|
183
|
+
const verified = jwt.verify(idToken, publicKey, {
|
|
184
|
+
algorithms: ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512'],
|
|
185
|
+
audience: this.config.clientId,
|
|
186
|
+
issuer: this.config.issuer || undefined,
|
|
187
|
+
clockTolerance: 60, // Allow 60 seconds clock skew
|
|
188
|
+
});
|
|
189
|
+
this.logger?.debug?.('ID token signature verified successfully');
|
|
190
|
+
return verified;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
// Signature verification failed - this is a security issue
|
|
194
|
+
this.logger?.error?.('ID token signature verification failed:', error.message);
|
|
195
|
+
throw new Error(`ID token verification failed: ${error.message}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
// No JWKS client - fall back to claims validation only
|
|
200
|
+
this.logger?.warn?.('JWKS not configured - verifying claims only, not signature');
|
|
201
|
+
return this.verifyIdTokenClaims(decoded.payload);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Verify ID token claims without signature verification
|
|
206
|
+
* Used as fallback when JWKS is not available
|
|
207
|
+
*/
|
|
208
|
+
verifyIdTokenClaims(payload) {
|
|
209
|
+
const now = Math.floor(Date.now() / 1000);
|
|
210
|
+
// Critical validations
|
|
211
|
+
if (!payload.exp || payload.exp < now) {
|
|
212
|
+
throw new Error('ID token expired');
|
|
213
|
+
}
|
|
214
|
+
if (!payload.iat || payload.iat > now + 60) {
|
|
215
|
+
throw new Error('ID token issued in the future');
|
|
216
|
+
}
|
|
217
|
+
if (!payload.aud) {
|
|
218
|
+
throw new Error('ID token missing audience');
|
|
219
|
+
}
|
|
220
|
+
const audience = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
|
|
221
|
+
if (!audience.includes(this.config.clientId)) {
|
|
222
|
+
throw new Error(`ID token audience mismatch`);
|
|
223
|
+
}
|
|
224
|
+
if (this.config.issuer && payload.iss !== this.config.issuer) {
|
|
225
|
+
throw new Error(`ID token issuer mismatch`);
|
|
226
|
+
}
|
|
227
|
+
if (!payload.sub) {
|
|
228
|
+
throw new Error('ID token missing subject');
|
|
229
|
+
}
|
|
230
|
+
return payload;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Map OAuth user info to Harper user object
|
|
234
|
+
*/
|
|
235
|
+
mapUserToHarper(userInfo) {
|
|
236
|
+
const username = this.extractClaim(userInfo, this.config.usernameClaim);
|
|
237
|
+
if (!username) {
|
|
238
|
+
throw new Error(`Username claim '${this.config.usernameClaim}' not found in user info`);
|
|
239
|
+
}
|
|
240
|
+
const role = this.extractClaim(userInfo, this.config.roleClaim) || this.config.defaultRole || 'user';
|
|
241
|
+
return {
|
|
242
|
+
username,
|
|
243
|
+
role,
|
|
244
|
+
provider: this.config.provider,
|
|
245
|
+
providerUserId: userInfo.sub || userInfo.id || userInfo.user_id,
|
|
246
|
+
email: userInfo.email,
|
|
247
|
+
name: userInfo.name || userInfo.display_name || userInfo.full_name,
|
|
248
|
+
metadata: {
|
|
249
|
+
oauthProvider: this.config.provider,
|
|
250
|
+
oauthClaims: userInfo,
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Extract a claim from user info (supports nested paths)
|
|
256
|
+
*/
|
|
257
|
+
extractClaim(userInfo, claimPath) {
|
|
258
|
+
if (!claimPath)
|
|
259
|
+
return null;
|
|
260
|
+
// Support nested paths like "profile.email"
|
|
261
|
+
const parts = claimPath.split('.');
|
|
262
|
+
let value = userInfo;
|
|
263
|
+
for (const part of parts) {
|
|
264
|
+
if (value && typeof value === 'object') {
|
|
265
|
+
value = value[part];
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return value;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Generate and store CSRF token for protection
|
|
275
|
+
*/
|
|
276
|
+
async generateCSRFToken(metadata = {}) {
|
|
277
|
+
const token = crypto.randomBytes(32).toString('hex');
|
|
278
|
+
const tokenData = {
|
|
279
|
+
timestamp: Date.now(),
|
|
280
|
+
...metadata,
|
|
281
|
+
};
|
|
282
|
+
// Store token (Harper handles expiration via table-level setting)
|
|
283
|
+
await csrfTokenManager.set(token, tokenData);
|
|
284
|
+
return token;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Verify and consume CSRF token
|
|
288
|
+
*/
|
|
289
|
+
async verifyCSRFToken(token) {
|
|
290
|
+
// Always use distributed storage
|
|
291
|
+
const tokenData = await csrfTokenManager.get(token);
|
|
292
|
+
if (tokenData) {
|
|
293
|
+
// Delete for one-time use
|
|
294
|
+
await csrfTokenManager.delete(token);
|
|
295
|
+
}
|
|
296
|
+
if (!tokenData) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
return tokenData;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Refresh an access token using a refresh token
|
|
303
|
+
*/
|
|
304
|
+
async refreshAccessToken(refreshToken) {
|
|
305
|
+
if (!refreshToken) {
|
|
306
|
+
throw new Error('Refresh token is required');
|
|
307
|
+
}
|
|
308
|
+
const params = new URLSearchParams({
|
|
309
|
+
grant_type: 'refresh_token',
|
|
310
|
+
refresh_token: refreshToken,
|
|
311
|
+
client_id: this.config.clientId,
|
|
312
|
+
client_secret: this.config.clientSecret,
|
|
313
|
+
});
|
|
314
|
+
// Some providers require scope to be included in refresh
|
|
315
|
+
if (this.config.scope) {
|
|
316
|
+
params.append('scope', this.config.scope);
|
|
317
|
+
}
|
|
318
|
+
const response = await fetch(this.config.tokenUrl, {
|
|
319
|
+
method: 'POST',
|
|
320
|
+
headers: {
|
|
321
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
322
|
+
'Accept': 'application/json',
|
|
323
|
+
},
|
|
324
|
+
body: params.toString(),
|
|
325
|
+
});
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
const contentType = response.headers.get('content-type');
|
|
328
|
+
let detail = `${response.status} ${response.statusText}`;
|
|
329
|
+
if (contentType?.includes('application/json')) {
|
|
330
|
+
try {
|
|
331
|
+
const errorBody = await response.json();
|
|
332
|
+
detail = errorBody.error_description || errorBody.error || response.statusText;
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// JSON parse failed despite content-type header — use status/statusText
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Drain unconsumed body to free the underlying socket (undici connection pool)
|
|
339
|
+
await response.body?.cancel();
|
|
340
|
+
this.logger?.error?.('Token refresh HTTP error:', {
|
|
341
|
+
status: response.status,
|
|
342
|
+
statusText: response.statusText,
|
|
343
|
+
detail,
|
|
344
|
+
});
|
|
345
|
+
throw new Error(`Token refresh failed: ${detail}`);
|
|
346
|
+
}
|
|
347
|
+
const contentType = response.headers.get('content-type');
|
|
348
|
+
let tokenData;
|
|
349
|
+
if (contentType?.includes('application/json')) {
|
|
350
|
+
tokenData = await response.json();
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
// Some providers return form-encoded data
|
|
354
|
+
const text = await response.text();
|
|
355
|
+
const tokenParams = new URLSearchParams(text);
|
|
356
|
+
tokenData = Object.fromEntries(tokenParams);
|
|
357
|
+
}
|
|
358
|
+
// Check if response contains an error (some providers return 200 with error object)
|
|
359
|
+
if (tokenData.error) {
|
|
360
|
+
this.logger?.error?.('Token refresh returned error in response:', {
|
|
361
|
+
error: tokenData.error,
|
|
362
|
+
error_description: tokenData.error_description,
|
|
363
|
+
error_uri: tokenData.error_uri,
|
|
364
|
+
});
|
|
365
|
+
throw new Error(`Token refresh failed: ${tokenData.error_description || tokenData.error}`);
|
|
366
|
+
}
|
|
367
|
+
return tokenData;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=OAuthProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthProvider.js","sourceRoot":"","sources":["../../src/lib/OAuthProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Configuration
|
|
3
|
+
*
|
|
4
|
+
* Provider configuration and initialization utilities
|
|
5
|
+
*/
|
|
6
|
+
import type { OAuthProviderConfig, OAuthPluginConfig, ProviderRegistry, Logger } from '../types.ts';
|
|
7
|
+
/**
|
|
8
|
+
* Expand environment variable in a string value
|
|
9
|
+
*
|
|
10
|
+
* If the value is a string in the format `${VAR_NAME}`, it will be replaced
|
|
11
|
+
* with the value of the environment variable. Non-string values are returned unchanged.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* expandEnvVar('${MY_VAR}') // Returns process.env.MY_VAR or '${MY_VAR}' if undefined
|
|
15
|
+
* expandEnvVar('literal') // Returns 'literal'
|
|
16
|
+
* expandEnvVar(123) // Returns 123
|
|
17
|
+
* expandEnvVar(true) // Returns true
|
|
18
|
+
*/
|
|
19
|
+
export declare function expandEnvVar(value: any): any;
|
|
20
|
+
/**
|
|
21
|
+
* Build configuration for a specific provider
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildProviderConfig(providerConfig: Record<string, any>, providerName: string, pluginDefaults?: Partial<OAuthProviderConfig>): OAuthProviderConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Extract plugin-level defaults from options
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractPluginDefaults(options: OAuthPluginConfig): Partial<OAuthProviderConfig>;
|
|
28
|
+
/**
|
|
29
|
+
* Initialize OAuth providers from configuration
|
|
30
|
+
*/
|
|
31
|
+
export declare function initializeProviders(options: OAuthPluginConfig, logger?: Logger): ProviderRegistry;
|