@technomoron/apicore-server 1.0.0-beta.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 +21 -0
- package/dist/cjs/api-module.cjs +34 -0
- package/dist/cjs/api-module.d.ts +45 -0
- package/dist/cjs/apicore-server.cjs +1561 -0
- package/dist/cjs/apicore-server.d.ts +288 -0
- package/dist/cjs/auth-api/auth-module.cjs +1248 -0
- package/dist/cjs/auth-api/auth-module.d.ts +116 -0
- package/dist/cjs/auth-api/compat-auth-storage.cjs +128 -0
- package/dist/cjs/auth-api/compat-auth-storage.d.ts +57 -0
- package/dist/cjs/auth-api/mem-auth-store.cjs +121 -0
- package/dist/cjs/auth-api/mem-auth-store.d.ts +68 -0
- package/dist/cjs/auth-api/module.cjs +25 -0
- package/dist/cjs/auth-api/module.d.ts +20 -0
- package/dist/cjs/auth-api/schemas.cjs +171 -0
- package/dist/cjs/auth-api/schemas.d.ts +21 -0
- package/dist/cjs/auth-api/sql-auth-store.cjs +179 -0
- package/dist/cjs/auth-api/sql-auth-store.d.ts +87 -0
- package/dist/cjs/auth-api/storage.cjs +102 -0
- package/dist/cjs/auth-api/storage.d.ts +38 -0
- package/dist/cjs/auth-api/types.cjs +2 -0
- package/dist/cjs/auth-api/types.d.ts +34 -0
- package/dist/cjs/auth-api/user-id.cjs +47 -0
- package/dist/cjs/auth-api/user-id.d.ts +5 -0
- package/dist/cjs/auth-cookie-options.cjs +66 -0
- package/dist/cjs/auth-cookie-options.d.ts +13 -0
- package/dist/cjs/base/client-info.cjs +285 -0
- package/dist/cjs/base/client-info.d.ts +27 -0
- package/dist/cjs/base/error-utils.cjs +50 -0
- package/dist/cjs/base/error-utils.d.ts +16 -0
- package/dist/cjs/base/request-utils.cjs +27 -0
- package/dist/cjs/base/request-utils.d.ts +8 -0
- package/dist/cjs/index.cjs +51 -0
- package/dist/cjs/index.d.ts +34 -0
- package/dist/cjs/limiter/auth-rate-limiter.cjs +35 -0
- package/dist/cjs/limiter/auth-rate-limiter.d.ts +12 -0
- package/dist/cjs/limiter/fixed-window.cjs +41 -0
- package/dist/cjs/limiter/fixed-window.d.ts +11 -0
- package/dist/cjs/oauth/base.cjs +7 -0
- package/dist/cjs/oauth/base.d.ts +17 -0
- package/dist/cjs/oauth/memory.cjs +135 -0
- package/dist/cjs/oauth/memory.d.ts +22 -0
- package/dist/cjs/oauth/models.cjs +47 -0
- package/dist/cjs/oauth/models.d.ts +50 -0
- package/dist/cjs/oauth/sequelize.cjs +159 -0
- package/dist/cjs/oauth/sequelize.d.ts +30 -0
- package/dist/cjs/oauth/types.cjs +3 -0
- package/dist/cjs/oauth/types.d.ts +51 -0
- package/dist/cjs/passkey/base.cjs +7 -0
- package/dist/cjs/passkey/base.d.ts +28 -0
- package/dist/cjs/passkey/config.cjs +26 -0
- package/dist/cjs/passkey/config.d.ts +2 -0
- package/dist/cjs/passkey/memory.cjs +123 -0
- package/dist/cjs/passkey/memory.d.ts +34 -0
- package/dist/cjs/passkey/models.cjs +142 -0
- package/dist/cjs/passkey/models.d.ts +34 -0
- package/dist/cjs/passkey/sequelize.cjs +126 -0
- package/dist/cjs/passkey/sequelize.d.ts +42 -0
- package/dist/cjs/passkey/service.cjs +413 -0
- package/dist/cjs/passkey/service.d.ts +21 -0
- package/dist/cjs/passkey/types.cjs +2 -0
- package/dist/cjs/passkey/types.d.ts +84 -0
- package/dist/cjs/sequelize-utils.cjs +56 -0
- package/dist/cjs/sequelize-utils.d.ts +8 -0
- package/dist/cjs/token/base.cjs +120 -0
- package/dist/cjs/token/base.d.ts +46 -0
- package/dist/cjs/token/memory.cjs +234 -0
- package/dist/cjs/token/memory.d.ts +29 -0
- package/dist/cjs/token/sequelize.cjs +400 -0
- package/dist/cjs/token/sequelize.d.ts +58 -0
- package/dist/cjs/token/types.cjs +2 -0
- package/dist/cjs/token/types.d.ts +34 -0
- package/dist/cjs/upload/memory.cjs +92 -0
- package/dist/cjs/upload/memory.d.ts +17 -0
- package/dist/cjs/upload/tus-module.cjs +270 -0
- package/dist/cjs/upload/tus-module.d.ts +38 -0
- package/dist/cjs/upload/types.cjs +2 -0
- package/dist/cjs/upload/types.d.ts +28 -0
- package/dist/cjs/user/base.cjs +53 -0
- package/dist/cjs/user/base.d.ts +36 -0
- package/dist/cjs/user/memory.cjs +194 -0
- package/dist/cjs/user/memory.d.ts +37 -0
- package/dist/cjs/user/sequelize.cjs +194 -0
- package/dist/cjs/user/sequelize.d.ts +46 -0
- package/dist/cjs/user/types.cjs +2 -0
- package/dist/cjs/user/types.d.ts +11 -0
- package/dist/esm/api-module.d.ts +45 -0
- package/dist/esm/api-module.js +30 -0
- package/dist/esm/apicore-server.d.ts +288 -0
- package/dist/esm/apicore-server.js +1552 -0
- package/dist/esm/auth-api/auth-module.d.ts +116 -0
- package/dist/esm/auth-api/auth-module.js +1246 -0
- package/dist/esm/auth-api/compat-auth-storage.d.ts +57 -0
- package/dist/esm/auth-api/compat-auth-storage.js +124 -0
- package/dist/esm/auth-api/mem-auth-store.d.ts +68 -0
- package/dist/esm/auth-api/mem-auth-store.js +117 -0
- package/dist/esm/auth-api/module.d.ts +20 -0
- package/dist/esm/auth-api/module.js +21 -0
- package/dist/esm/auth-api/schemas.d.ts +21 -0
- package/dist/esm/auth-api/schemas.js +168 -0
- package/dist/esm/auth-api/sql-auth-store.d.ts +87 -0
- package/dist/esm/auth-api/sql-auth-store.js +175 -0
- package/dist/esm/auth-api/storage.d.ts +38 -0
- package/dist/esm/auth-api/storage.js +98 -0
- package/dist/esm/auth-api/types.d.ts +34 -0
- package/dist/esm/auth-api/types.js +1 -0
- package/dist/esm/auth-api/user-id.d.ts +5 -0
- package/dist/esm/auth-api/user-id.js +41 -0
- package/dist/esm/auth-cookie-options.d.ts +13 -0
- package/dist/esm/auth-cookie-options.js +63 -0
- package/dist/esm/base/client-info.d.ts +27 -0
- package/dist/esm/base/client-info.js +282 -0
- package/dist/esm/base/error-utils.d.ts +16 -0
- package/dist/esm/base/error-utils.js +44 -0
- package/dist/esm/base/request-utils.d.ts +8 -0
- package/dist/esm/base/request-utils.js +23 -0
- package/dist/esm/index.d.ts +34 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/limiter/auth-rate-limiter.d.ts +12 -0
- package/dist/esm/limiter/auth-rate-limiter.js +32 -0
- package/dist/esm/limiter/fixed-window.d.ts +11 -0
- package/dist/esm/limiter/fixed-window.js +37 -0
- package/dist/esm/oauth/base.d.ts +17 -0
- package/dist/esm/oauth/base.js +3 -0
- package/dist/esm/oauth/memory.d.ts +22 -0
- package/dist/esm/oauth/memory.js +128 -0
- package/dist/esm/oauth/models.d.ts +50 -0
- package/dist/esm/oauth/models.js +38 -0
- package/dist/esm/oauth/sequelize.d.ts +30 -0
- package/dist/esm/oauth/sequelize.js +148 -0
- package/dist/esm/oauth/types.d.ts +51 -0
- package/dist/esm/oauth/types.js +2 -0
- package/dist/esm/passkey/base.d.ts +28 -0
- package/dist/esm/passkey/base.js +3 -0
- package/dist/esm/passkey/config.d.ts +2 -0
- package/dist/esm/passkey/config.js +23 -0
- package/dist/esm/passkey/memory.d.ts +34 -0
- package/dist/esm/passkey/memory.js +119 -0
- package/dist/esm/passkey/models.d.ts +34 -0
- package/dist/esm/passkey/models.js +135 -0
- package/dist/esm/passkey/sequelize.d.ts +42 -0
- package/dist/esm/passkey/sequelize.js +122 -0
- package/dist/esm/passkey/service.d.ts +21 -0
- package/dist/esm/passkey/service.js +376 -0
- package/dist/esm/passkey/types.d.ts +84 -0
- package/dist/esm/passkey/types.js +1 -0
- package/dist/esm/sequelize-utils.d.ts +8 -0
- package/dist/esm/sequelize-utils.js +47 -0
- package/dist/esm/token/base.d.ts +46 -0
- package/dist/esm/token/base.js +113 -0
- package/dist/esm/token/memory.d.ts +29 -0
- package/dist/esm/token/memory.js +230 -0
- package/dist/esm/token/sequelize.d.ts +58 -0
- package/dist/esm/token/sequelize.js +396 -0
- package/dist/esm/token/types.d.ts +34 -0
- package/dist/esm/token/types.js +1 -0
- package/dist/esm/upload/memory.d.ts +17 -0
- package/dist/esm/upload/memory.js +86 -0
- package/dist/esm/upload/tus-module.d.ts +38 -0
- package/dist/esm/upload/tus-module.js +266 -0
- package/dist/esm/upload/types.d.ts +28 -0
- package/dist/esm/upload/types.js +1 -0
- package/dist/esm/user/base.d.ts +36 -0
- package/dist/esm/user/base.js +46 -0
- package/dist/esm/user/memory.d.ts +37 -0
- package/dist/esm/user/memory.js +190 -0
- package/dist/esm/user/sequelize.d.ts +46 -0
- package/dist/esm/user/sequelize.js +188 -0
- package/dist/esm/user/types.d.ts +11 -0
- package/dist/esm/user/types.js +1 -0
- package/docs/swagger/openapi.json +2162 -0
- package/package.json +131 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type ApiRequest, type ApiRoute, type ApiServer } from '../apicore-server.js';
|
|
2
|
+
import { BaseAuthModule, type AuthProviderModule } from './module.js';
|
|
3
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
4
|
+
import type { OAuthCallbackParams, OAuthCallbackResult, OAuthStartParams, OAuthStartResult } from '../oauth/types.js';
|
|
5
|
+
import type { TokenPair, Token } from '../token/types.js';
|
|
6
|
+
interface CanImpersonateContext<UserEntity> {
|
|
7
|
+
apiReq: ApiRequest;
|
|
8
|
+
realUser: UserEntity;
|
|
9
|
+
realUserId: AuthIdentifier;
|
|
10
|
+
targetUser: UserEntity;
|
|
11
|
+
effectiveUserId: AuthIdentifier;
|
|
12
|
+
}
|
|
13
|
+
type AuthRateLimitEndpoint = 'login' | 'passkey-challenge' | 'oauth-token' | 'oauth-authorize';
|
|
14
|
+
interface AuthModuleOptions<UserEntity> {
|
|
15
|
+
namespace?: string;
|
|
16
|
+
defaultDomain?: string;
|
|
17
|
+
canImpersonate?: (context: CanImpersonateContext<UserEntity>) => Promise<boolean> | boolean;
|
|
18
|
+
rateLimit?: (context: {
|
|
19
|
+
apiReq: ApiRequest;
|
|
20
|
+
endpoint: AuthRateLimitEndpoint;
|
|
21
|
+
}) => Promise<void> | void;
|
|
22
|
+
allowInsecurePkcePlain?: boolean;
|
|
23
|
+
}
|
|
24
|
+
type TokenMetadata = Partial<Token> & {
|
|
25
|
+
sessionCookie?: boolean;
|
|
26
|
+
};
|
|
27
|
+
interface TokenIssueOptions extends TokenMetadata {
|
|
28
|
+
expires?: Date;
|
|
29
|
+
sessionCookie?: boolean;
|
|
30
|
+
}
|
|
31
|
+
interface NormalizedTokenMetadata extends TokenMetadata {
|
|
32
|
+
domain: string;
|
|
33
|
+
fingerprint: string;
|
|
34
|
+
label: string;
|
|
35
|
+
browser: string;
|
|
36
|
+
device: string;
|
|
37
|
+
ip: string;
|
|
38
|
+
os: string;
|
|
39
|
+
}
|
|
40
|
+
type TokenClaims = TokenMetadata & {
|
|
41
|
+
uid: string;
|
|
42
|
+
exp?: number;
|
|
43
|
+
iat?: number;
|
|
44
|
+
};
|
|
45
|
+
type AuthCapableServer<PublicUser> = ApiServer & {
|
|
46
|
+
initiateOAuth?: (params: OAuthStartParams) => Promise<OAuthStartResult>;
|
|
47
|
+
completeOAuth?: (params: OAuthCallbackParams) => Promise<OAuthCallbackResult<PublicUser>>;
|
|
48
|
+
};
|
|
49
|
+
export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<UserEntity> implements AuthProviderModule<UserEntity> {
|
|
50
|
+
static defaultNamespace: string;
|
|
51
|
+
get server(): AuthCapableServer<PublicUser>;
|
|
52
|
+
set server(value: AuthCapableServer<PublicUser>);
|
|
53
|
+
private readonly defaultDomain?;
|
|
54
|
+
private readonly canImpersonateHook?;
|
|
55
|
+
private readonly rateLimitHook?;
|
|
56
|
+
private readonly allowInsecurePkcePlain;
|
|
57
|
+
constructor(options?: AuthModuleOptions<UserEntity>);
|
|
58
|
+
protected get storage(): AuthAdapter<UserEntity, PublicUser>;
|
|
59
|
+
protected canImpersonate(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<boolean>;
|
|
60
|
+
protected ensureImpersonationAllowed(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<void>;
|
|
61
|
+
protected buildTokenPayload(user: UserEntity, metadata?: TokenMetadata): TokenClaims;
|
|
62
|
+
protected buildTokenMetadata(metadata?: TokenMetadata): NormalizedTokenMetadata;
|
|
63
|
+
protected enrichTokenMetadata(apiReq: ApiRequest, metadata?: TokenMetadata): TokenMetadata;
|
|
64
|
+
private sessionRefreshTtlSeconds;
|
|
65
|
+
private normalizeRefreshTtlSeconds;
|
|
66
|
+
private resolveSessionPreferences;
|
|
67
|
+
private mergeSessionPreferences;
|
|
68
|
+
private sessionPrefsFromRecord;
|
|
69
|
+
private validateCredentialId;
|
|
70
|
+
private normalizeCredentialId;
|
|
71
|
+
private toIsoDate;
|
|
72
|
+
private cookieOptions;
|
|
73
|
+
private setJwtCookies;
|
|
74
|
+
issueTokens(apiReq: ApiRequest, user: UserEntity, metadata?: TokenIssueOptions): Promise<TokenPair>;
|
|
75
|
+
private assertAuthReady;
|
|
76
|
+
private parseLoginBody;
|
|
77
|
+
private parseImpersonationRequest;
|
|
78
|
+
private resolveImpersonationIdentifier;
|
|
79
|
+
private buildImpersonationMetadata;
|
|
80
|
+
private getUserOrThrow;
|
|
81
|
+
private getRealUserIdentifier;
|
|
82
|
+
private resolveActorContext;
|
|
83
|
+
private extractRefreshToken;
|
|
84
|
+
private normalizeScope;
|
|
85
|
+
private postLogin;
|
|
86
|
+
private postRefresh;
|
|
87
|
+
private postLogout;
|
|
88
|
+
private postWhoAmI;
|
|
89
|
+
private postPasskeyChallenge;
|
|
90
|
+
private postPasskeyVerify;
|
|
91
|
+
private getPasskeys;
|
|
92
|
+
private deletePasskey;
|
|
93
|
+
private postImpersonation;
|
|
94
|
+
private deleteImpersonation;
|
|
95
|
+
private getUserFromPasskey;
|
|
96
|
+
private postOAuthStart;
|
|
97
|
+
private postOAuthCallback;
|
|
98
|
+
private postOAuthAuthorize;
|
|
99
|
+
private postOAuthToken;
|
|
100
|
+
private handleAuthorizationCodeGrant;
|
|
101
|
+
private handleRefreshTokenGrant;
|
|
102
|
+
private clearOAuthCookies;
|
|
103
|
+
private buildTokenResponse;
|
|
104
|
+
private resolveScope;
|
|
105
|
+
private resolveClientAuthentication;
|
|
106
|
+
private assertRedirectUriAllowed;
|
|
107
|
+
private resolveUserForOAuth;
|
|
108
|
+
private hasPasskeyService;
|
|
109
|
+
private hasOAuthStore;
|
|
110
|
+
private storageImplements;
|
|
111
|
+
private storageImplementsAll;
|
|
112
|
+
private applyRateLimit;
|
|
113
|
+
private resolvePkceChallengeMethod;
|
|
114
|
+
defineRoutes(): ApiRoute[];
|
|
115
|
+
}
|
|
116
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CompositeAuthAdapter = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const service_js_1 = require("../passkey/service.cjs");
|
|
6
|
+
class CompositeAuthAdapter {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.userStore = options.userStore;
|
|
9
|
+
this.tokenStore = options.tokenStore;
|
|
10
|
+
this.oauthStore = options.oauthStore;
|
|
11
|
+
this.canImpersonateFn = options.canImpersonate;
|
|
12
|
+
if (options.passkeys instanceof service_js_1.PasskeyService) {
|
|
13
|
+
this.passkeyService = options.passkeys;
|
|
14
|
+
}
|
|
15
|
+
else if (options.passkeys) {
|
|
16
|
+
this.passkeyService = new service_js_1.PasskeyService(options.passkeys.config, options.passkeys.store);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async getUser(identifier) {
|
|
20
|
+
return this.userStore.findUser(identifier);
|
|
21
|
+
}
|
|
22
|
+
getUserPasswordHash(user) {
|
|
23
|
+
return this.userStore.getPasswordHash(user) ?? '';
|
|
24
|
+
}
|
|
25
|
+
getUserId(user) {
|
|
26
|
+
return this.userStore.getUserId(user);
|
|
27
|
+
}
|
|
28
|
+
filterUser(user) {
|
|
29
|
+
return this.userStore.toPublic(user);
|
|
30
|
+
}
|
|
31
|
+
async verifyPassword(password, hash) {
|
|
32
|
+
return this.userStore.verifyPassword(password, hash);
|
|
33
|
+
}
|
|
34
|
+
async storeToken(data) {
|
|
35
|
+
return this.tokenStore.save(data);
|
|
36
|
+
}
|
|
37
|
+
async getToken(query, opts) {
|
|
38
|
+
return this.tokenStore.get(query, opts);
|
|
39
|
+
}
|
|
40
|
+
async deleteToken(query) {
|
|
41
|
+
return this.tokenStore.delete(query);
|
|
42
|
+
}
|
|
43
|
+
async updateToken(updates) {
|
|
44
|
+
return this.tokenStore.update(updates);
|
|
45
|
+
}
|
|
46
|
+
async createPasskeyChallenge(params) {
|
|
47
|
+
if (!this.passkeyService) {
|
|
48
|
+
throw new Error('Passkey storage is not configured');
|
|
49
|
+
}
|
|
50
|
+
return this.passkeyService.createChallenge(params);
|
|
51
|
+
}
|
|
52
|
+
async verifyPasskeyResponse(params) {
|
|
53
|
+
if (!this.passkeyService) {
|
|
54
|
+
throw new Error('Passkey storage is not configured');
|
|
55
|
+
}
|
|
56
|
+
return this.passkeyService.verifyResponse(params);
|
|
57
|
+
}
|
|
58
|
+
async listUserCredentials(userId) {
|
|
59
|
+
if (!this.passkeyService) {
|
|
60
|
+
throw new Error('Passkey storage is not configured');
|
|
61
|
+
}
|
|
62
|
+
return this.passkeyService.listUserCredentials(userId);
|
|
63
|
+
}
|
|
64
|
+
async deletePasskeyCredential(credentialId) {
|
|
65
|
+
if (!this.passkeyService) {
|
|
66
|
+
throw new Error('Passkey storage is not configured');
|
|
67
|
+
}
|
|
68
|
+
return this.passkeyService.deleteCredential(credentialId);
|
|
69
|
+
}
|
|
70
|
+
async getClient(clientId) {
|
|
71
|
+
if (!this.oauthStore) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return this.oauthStore.getClient(clientId);
|
|
75
|
+
}
|
|
76
|
+
async verifyClientSecret(client, clientSecret) {
|
|
77
|
+
if (!this.oauthStore) {
|
|
78
|
+
throw new Error('OAuth storage is not configured');
|
|
79
|
+
}
|
|
80
|
+
return this.oauthStore.verifyClientSecret(client.clientId, clientSecret);
|
|
81
|
+
}
|
|
82
|
+
async createAuthCode(request) {
|
|
83
|
+
if (!this.oauthStore) {
|
|
84
|
+
throw new Error('OAuth storage is not configured');
|
|
85
|
+
}
|
|
86
|
+
const expiresAt = new Date(Date.now() + (request.expiresInSeconds ?? 300) * 1000);
|
|
87
|
+
const code = request.code ?? (0, node_crypto_1.randomUUID)();
|
|
88
|
+
await this.oauthStore.createAuthCode({
|
|
89
|
+
code,
|
|
90
|
+
clientId: request.clientId,
|
|
91
|
+
userId: request.userId,
|
|
92
|
+
scope: request.scope,
|
|
93
|
+
redirectUri: request.redirectUri,
|
|
94
|
+
codeChallenge: request.codeChallenge,
|
|
95
|
+
codeChallengeMethod: request.codeChallengeMethod,
|
|
96
|
+
expiresAt,
|
|
97
|
+
metadata: request.metadata
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
code,
|
|
101
|
+
clientId: request.clientId,
|
|
102
|
+
userId: request.userId,
|
|
103
|
+
redirectUri: request.redirectUri,
|
|
104
|
+
scope: request.scope ?? [],
|
|
105
|
+
codeChallenge: request.codeChallenge,
|
|
106
|
+
codeChallengeMethod: request.codeChallengeMethod,
|
|
107
|
+
expiresAt,
|
|
108
|
+
metadata: request.metadata
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async consumeAuthCode(code, clientId) {
|
|
112
|
+
if (!this.oauthStore) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const consumed = await this.oauthStore.consumeAuthCode(code, clientId);
|
|
116
|
+
if (!consumed) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
return consumed;
|
|
120
|
+
}
|
|
121
|
+
async canImpersonate(params) {
|
|
122
|
+
if (this.canImpersonateFn) {
|
|
123
|
+
return !!(await this.canImpersonateFn(params));
|
|
124
|
+
}
|
|
125
|
+
return String(params.realUserId) === String(params.effectiveUserId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.CompositeAuthAdapter = CompositeAuthAdapter;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { PasskeyService } from '../passkey/service.js';
|
|
2
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
3
|
+
import type { OAuthStore } from '../oauth/base.js';
|
|
4
|
+
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
5
|
+
import type { PasskeyStore } from '../passkey/base.js';
|
|
6
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
7
|
+
import type { TokenStore } from '../token/base.js';
|
|
8
|
+
import type { Token } from '../token/types.js';
|
|
9
|
+
import type { UserStore } from '../user/base.js';
|
|
10
|
+
interface PasskeyAdapterOptions {
|
|
11
|
+
store: PasskeyStore;
|
|
12
|
+
config: PasskeyServiceConfig;
|
|
13
|
+
}
|
|
14
|
+
export interface AuthAdapterOptions<UserRow, PublicUser> {
|
|
15
|
+
userStore: UserStore<UserRow, PublicUser>;
|
|
16
|
+
tokenStore: TokenStore;
|
|
17
|
+
passkeys?: PasskeyAdapterOptions | PasskeyService;
|
|
18
|
+
oauthStore?: OAuthStore;
|
|
19
|
+
canImpersonate?: (params: {
|
|
20
|
+
realUserId: AuthIdentifier;
|
|
21
|
+
effectiveUserId: AuthIdentifier;
|
|
22
|
+
}) => boolean | Promise<boolean>;
|
|
23
|
+
}
|
|
24
|
+
export declare class CompositeAuthAdapter<UserRow, PublicUser> implements AuthAdapter<UserRow, PublicUser> {
|
|
25
|
+
private readonly userStore;
|
|
26
|
+
private readonly tokenStore;
|
|
27
|
+
private readonly oauthStore?;
|
|
28
|
+
private readonly passkeyService?;
|
|
29
|
+
private readonly canImpersonateFn?;
|
|
30
|
+
constructor(options: AuthAdapterOptions<UserRow, PublicUser>);
|
|
31
|
+
getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
|
|
32
|
+
getUserPasswordHash(user: UserRow): string;
|
|
33
|
+
getUserId(user: UserRow): AuthIdentifier;
|
|
34
|
+
filterUser(user: UserRow): PublicUser;
|
|
35
|
+
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
36
|
+
storeToken(data: Token): Promise<void>;
|
|
37
|
+
getToken(query: Partial<Token>, opts?: {
|
|
38
|
+
includeExpired?: boolean;
|
|
39
|
+
}): Promise<Token | null>;
|
|
40
|
+
deleteToken(query: Partial<Token>): Promise<number>;
|
|
41
|
+
updateToken(updates: Partial<Token> & {
|
|
42
|
+
refreshToken: string;
|
|
43
|
+
}): Promise<boolean>;
|
|
44
|
+
createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
45
|
+
verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
46
|
+
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
47
|
+
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
48
|
+
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
49
|
+
verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
50
|
+
createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
51
|
+
consumeAuthCode(code: string, clientId: string): Promise<AuthCodeData | null>;
|
|
52
|
+
canImpersonate(params: {
|
|
53
|
+
realUserId: AuthIdentifier;
|
|
54
|
+
effectiveUserId: AuthIdentifier;
|
|
55
|
+
}): Promise<boolean>;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemAuthStore = void 0;
|
|
4
|
+
const memory_js_1 = require("../oauth/memory.cjs");
|
|
5
|
+
const config_js_1 = require("../passkey/config.cjs");
|
|
6
|
+
const memory_js_2 = require("../passkey/memory.cjs");
|
|
7
|
+
const memory_js_3 = require("../token/memory.cjs");
|
|
8
|
+
const memory_js_4 = require("../user/memory.cjs");
|
|
9
|
+
const compat_auth_storage_js_1 = require("./compat-auth-storage.cjs");
|
|
10
|
+
const user_id_js_1 = require("./user-id.cjs");
|
|
11
|
+
class MemAuthStore {
|
|
12
|
+
constructor(params = {}) {
|
|
13
|
+
this.userStore = new memory_js_4.MemoryUserStore({
|
|
14
|
+
toPublic: params.publicUserMapper ?? params.toPublic,
|
|
15
|
+
userIdFactory: params.userIdFactory,
|
|
16
|
+
startingUserId: params.startingUserId,
|
|
17
|
+
bcryptRounds: params.bcryptRounds,
|
|
18
|
+
bcryptPepper: params.passwordPepper
|
|
19
|
+
});
|
|
20
|
+
this.tokenStore = params.tokenStore ?? new memory_js_3.MemoryTokenStore();
|
|
21
|
+
this.oauthStore = new memory_js_1.MemoryOAuthStore({ bcryptRounds: params.bcryptRounds });
|
|
22
|
+
let passkeyStore;
|
|
23
|
+
let passkeyConfig;
|
|
24
|
+
if (params.passkeys !== false) {
|
|
25
|
+
passkeyConfig = (0, config_js_1.normalizePasskeyConfig)(params.passkeys ?? {});
|
|
26
|
+
const resolveUser = async (lookup) => {
|
|
27
|
+
const found = await this.userStore.findUser(lookup.userId ?? lookup.login ?? '');
|
|
28
|
+
if (!found) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const mapper = params.passkeyUserMapper ??
|
|
32
|
+
((user) => ({
|
|
33
|
+
id: user.user_id,
|
|
34
|
+
login: user.login,
|
|
35
|
+
displayName: user.login
|
|
36
|
+
}));
|
|
37
|
+
return mapper(found);
|
|
38
|
+
};
|
|
39
|
+
passkeyStore = new memory_js_2.MemoryPasskeyStore({ resolveUser });
|
|
40
|
+
this.passkeyStore = passkeyStore;
|
|
41
|
+
}
|
|
42
|
+
this.adapter = new compat_auth_storage_js_1.CompositeAuthAdapter({
|
|
43
|
+
userStore: this.userStore,
|
|
44
|
+
tokenStore: this.tokenStore,
|
|
45
|
+
passkeys: passkeyStore && passkeyConfig ? { store: passkeyStore, config: passkeyConfig } : undefined,
|
|
46
|
+
oauthStore: this.oauthStore,
|
|
47
|
+
canImpersonate: params.canImpersonate
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async initialise() {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
async close() {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
async getUser(identifier) {
|
|
57
|
+
return this.adapter.getUser(identifier);
|
|
58
|
+
}
|
|
59
|
+
getUserPasswordHash(user) {
|
|
60
|
+
return this.adapter.getUserPasswordHash(user);
|
|
61
|
+
}
|
|
62
|
+
getUserId(user) {
|
|
63
|
+
return this.adapter.getUserId(user);
|
|
64
|
+
}
|
|
65
|
+
filterUser(user) {
|
|
66
|
+
return this.adapter.filterUser(user);
|
|
67
|
+
}
|
|
68
|
+
async verifyPassword(password, hash) {
|
|
69
|
+
return this.adapter.verifyPassword(password, hash);
|
|
70
|
+
}
|
|
71
|
+
async storeToken(data) {
|
|
72
|
+
return this.adapter.storeToken(data);
|
|
73
|
+
}
|
|
74
|
+
async getToken(query, opts) {
|
|
75
|
+
const normalized = {
|
|
76
|
+
...query,
|
|
77
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
78
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
79
|
+
};
|
|
80
|
+
return this.adapter.getToken(normalized, opts);
|
|
81
|
+
}
|
|
82
|
+
async deleteToken(query) {
|
|
83
|
+
const normalized = {
|
|
84
|
+
...query,
|
|
85
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
86
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
87
|
+
};
|
|
88
|
+
return this.adapter.deleteToken(normalized);
|
|
89
|
+
}
|
|
90
|
+
async updateToken(updates) {
|
|
91
|
+
return this.adapter.updateToken(updates);
|
|
92
|
+
}
|
|
93
|
+
async createPasskeyChallenge(params) {
|
|
94
|
+
return this.adapter.createPasskeyChallenge(params);
|
|
95
|
+
}
|
|
96
|
+
async verifyPasskeyResponse(params) {
|
|
97
|
+
return this.adapter.verifyPasskeyResponse(params);
|
|
98
|
+
}
|
|
99
|
+
async listUserCredentials(userId) {
|
|
100
|
+
return this.adapter.listUserCredentials(userId);
|
|
101
|
+
}
|
|
102
|
+
async deletePasskeyCredential(credentialId) {
|
|
103
|
+
return this.adapter.deletePasskeyCredential(credentialId);
|
|
104
|
+
}
|
|
105
|
+
async getClient(clientId) {
|
|
106
|
+
return this.adapter.getClient(clientId);
|
|
107
|
+
}
|
|
108
|
+
async verifyClientSecret(client, clientSecret) {
|
|
109
|
+
return this.adapter.verifyClientSecret(client, clientSecret);
|
|
110
|
+
}
|
|
111
|
+
async createAuthCode(request) {
|
|
112
|
+
return this.adapter.createAuthCode(request);
|
|
113
|
+
}
|
|
114
|
+
async consumeAuthCode(code, clientId) {
|
|
115
|
+
return this.adapter.consumeAuthCode(code, clientId);
|
|
116
|
+
}
|
|
117
|
+
async canImpersonate(params) {
|
|
118
|
+
return this.adapter.canImpersonate(params);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.MemAuthStore = MemAuthStore;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { MemoryOAuthStore } from '../oauth/memory.js';
|
|
2
|
+
import { MemoryPasskeyStore } from '../passkey/memory.js';
|
|
3
|
+
import { TokenStore } from '../token/base.js';
|
|
4
|
+
import { MemoryUserStore } from '../user/memory.js';
|
|
5
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
6
|
+
import type { MemoryOAuthStoreOptions } from '../oauth/memory.js';
|
|
7
|
+
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
8
|
+
import type { MemoryPasskeyStoreOptions } from '../passkey/memory.js';
|
|
9
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
10
|
+
import type { Token } from '../token/types.js';
|
|
11
|
+
import type { MemoryUserAttributes, MemoryPublicUser, MemoryUserStoreOptions } from '../user/memory.js';
|
|
12
|
+
interface PasskeyOptions extends Partial<PasskeyServiceConfig> {
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface MemAuthStoreParams<UserAttributes extends MemoryUserAttributes = MemoryUserAttributes, PublicUserShape extends MemoryPublicUser<UserAttributes> = MemoryPublicUser<UserAttributes>> extends Omit<MemoryUserStoreOptions<UserAttributes, PublicUserShape>, 'hasher'>, MemoryOAuthStoreOptions, Partial<MemoryPasskeyStoreOptions> {
|
|
16
|
+
bcryptRounds?: number;
|
|
17
|
+
passwordPepper?: string;
|
|
18
|
+
publicUserMapper?: (user: UserAttributes) => PublicUserShape;
|
|
19
|
+
passkeyUserMapper?: (user: UserAttributes) => PasskeyUserDescriptor;
|
|
20
|
+
passkeys?: false | PasskeyOptions;
|
|
21
|
+
canImpersonate?: (params: {
|
|
22
|
+
realUserId: AuthIdentifier;
|
|
23
|
+
effectiveUserId: AuthIdentifier;
|
|
24
|
+
}) => boolean | Promise<boolean>;
|
|
25
|
+
tokenStore?: TokenStore;
|
|
26
|
+
}
|
|
27
|
+
export declare class MemAuthStore<UserAttributes extends MemoryUserAttributes = MemoryUserAttributes, PublicUserShape extends MemoryPublicUser<UserAttributes> = MemoryPublicUser<UserAttributes>> implements AuthAdapter<UserAttributes, PublicUserShape> {
|
|
28
|
+
readonly userStore: MemoryUserStore<UserAttributes, PublicUserShape>;
|
|
29
|
+
readonly tokenStore: TokenStore;
|
|
30
|
+
readonly passkeyStore?: MemoryPasskeyStore;
|
|
31
|
+
readonly oauthStore: MemoryOAuthStore;
|
|
32
|
+
private readonly adapter;
|
|
33
|
+
constructor(params?: MemAuthStoreParams<UserAttributes, PublicUserShape>);
|
|
34
|
+
initialise(): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
getUser(identifier: AuthIdentifier): Promise<UserAttributes | null>;
|
|
37
|
+
getUserPasswordHash(user: UserAttributes): string;
|
|
38
|
+
getUserId(user: UserAttributes): AuthIdentifier;
|
|
39
|
+
filterUser(user: UserAttributes): PublicUserShape;
|
|
40
|
+
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
41
|
+
storeToken(data: Token): Promise<void>;
|
|
42
|
+
getToken(query: Partial<Token> & {
|
|
43
|
+
userId?: AuthIdentifier;
|
|
44
|
+
ruid?: AuthIdentifier;
|
|
45
|
+
}, opts?: {
|
|
46
|
+
includeExpired?: boolean;
|
|
47
|
+
}): Promise<Token | null>;
|
|
48
|
+
deleteToken(query: Partial<Token> & {
|
|
49
|
+
userId?: AuthIdentifier;
|
|
50
|
+
ruid?: AuthIdentifier;
|
|
51
|
+
}): Promise<number>;
|
|
52
|
+
updateToken(updates: Partial<Token> & {
|
|
53
|
+
refreshToken: string;
|
|
54
|
+
}): Promise<boolean>;
|
|
55
|
+
createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
56
|
+
verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
57
|
+
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
58
|
+
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
59
|
+
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
60
|
+
verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
61
|
+
createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
62
|
+
consumeAuthCode(code: string, clientId: string): Promise<AuthCodeData | null>;
|
|
63
|
+
canImpersonate(params: {
|
|
64
|
+
realUserId: AuthIdentifier;
|
|
65
|
+
effectiveUserId: AuthIdentifier;
|
|
66
|
+
}): Promise<boolean>;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nullAuthModule = exports.BaseAuthModule = void 0;
|
|
4
|
+
const api_module_js_1 = require("../api-module.cjs");
|
|
5
|
+
// Handy base that you can extend when wiring a real auth module. Subclasses
|
|
6
|
+
// must supply a namespace via the constructor and implement token issuance.
|
|
7
|
+
class BaseAuthModule extends api_module_js_1.ApiModule {
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
super(opts);
|
|
10
|
+
this.moduleType = 'auth';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.BaseAuthModule = BaseAuthModule;
|
|
14
|
+
class NullAuthModule extends BaseAuthModule {
|
|
15
|
+
constructor() {
|
|
16
|
+
super({ namespace: '__null__' });
|
|
17
|
+
}
|
|
18
|
+
async issueTokens(apiReq, user, metadata) {
|
|
19
|
+
void apiReq;
|
|
20
|
+
void user;
|
|
21
|
+
void metadata;
|
|
22
|
+
throw new Error('Auth module not configured. Inject a real auth module before issuing tokens.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.nullAuthModule = new NullAuthModule();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ApiModule } from '../api-module.js';
|
|
2
|
+
import type { ApiRequest } from '../apicore-server.js';
|
|
3
|
+
import type { Token, TokenPair } from '../token/types.js';
|
|
4
|
+
export interface AuthProviderModule<UserRow = unknown> {
|
|
5
|
+
readonly moduleType: 'auth';
|
|
6
|
+
readonly namespace: string;
|
|
7
|
+
issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: Partial<Token> & {
|
|
8
|
+
expires?: Date;
|
|
9
|
+
}): Promise<TokenPair>;
|
|
10
|
+
}
|
|
11
|
+
export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule implements AuthProviderModule<UserRow> {
|
|
12
|
+
readonly moduleType: "auth";
|
|
13
|
+
protected constructor(opts: {
|
|
14
|
+
namespace: string;
|
|
15
|
+
});
|
|
16
|
+
abstract issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: Partial<Token> & {
|
|
17
|
+
expires?: Date;
|
|
18
|
+
}): Promise<TokenPair>;
|
|
19
|
+
}
|
|
20
|
+
export declare const nullAuthModule: AuthProviderModule<unknown>;
|