@technomoron/api-server-base 2.0.0-beta.2 → 2.0.0-beta.4
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/cjs/api-server-base.cjs +2 -2
- package/dist/cjs/api-server-base.d.ts +4 -4
- package/dist/cjs/auth-api/auth-module.d.ts +11 -2
- package/dist/cjs/auth-api/auth-module.js +179 -43
- package/dist/cjs/auth-api/compat-auth-storage.d.ts +7 -5
- package/dist/cjs/auth-api/compat-auth-storage.js +15 -3
- package/dist/cjs/auth-api/mem-auth-store.d.ts +5 -3
- package/dist/cjs/auth-api/mem-auth-store.js +7 -1
- package/dist/cjs/auth-api/sql-auth-store.d.ts +5 -3
- package/dist/cjs/auth-api/sql-auth-store.js +7 -1
- package/dist/cjs/auth-api/storage.d.ts +6 -4
- package/dist/cjs/auth-api/storage.js +15 -5
- package/dist/cjs/auth-api/types.d.ts +7 -2
- package/dist/cjs/index.cjs +4 -4
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/passkey/base.d.ts +1 -0
- package/dist/cjs/passkey/memory.d.ts +1 -0
- package/dist/cjs/passkey/memory.js +4 -0
- package/dist/cjs/passkey/sequelize.d.ts +1 -0
- package/dist/cjs/passkey/sequelize.js +11 -2
- package/dist/cjs/passkey/service.d.ts +5 -2
- package/dist/cjs/passkey/service.js +6 -0
- package/dist/cjs/passkey/types.d.ts +3 -0
- package/dist/esm/api-server-base.d.ts +4 -4
- package/dist/esm/api-server-base.js +3 -3
- package/dist/esm/auth-api/auth-module.d.ts +11 -2
- package/dist/esm/auth-api/auth-module.js +179 -43
- package/dist/esm/auth-api/compat-auth-storage.d.ts +7 -5
- package/dist/esm/auth-api/compat-auth-storage.js +13 -1
- package/dist/esm/auth-api/mem-auth-store.d.ts +5 -3
- package/dist/esm/auth-api/mem-auth-store.js +8 -2
- package/dist/esm/auth-api/sql-auth-store.d.ts +5 -3
- package/dist/esm/auth-api/sql-auth-store.js +8 -2
- package/dist/esm/auth-api/storage.d.ts +6 -4
- package/dist/esm/auth-api/storage.js +13 -3
- package/dist/esm/auth-api/types.d.ts +7 -2
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +2 -2
- package/dist/esm/passkey/base.d.ts +1 -0
- package/dist/esm/passkey/memory.d.ts +1 -0
- package/dist/esm/passkey/memory.js +4 -0
- package/dist/esm/passkey/sequelize.d.ts +1 -0
- package/dist/esm/passkey/sequelize.js +11 -2
- package/dist/esm/passkey/service.d.ts +5 -2
- package/dist/esm/passkey/service.js +6 -0
- package/dist/esm/passkey/types.d.ts +3 -0
- package/package.json +1 -1
|
@@ -370,7 +370,7 @@ class ApiServer {
|
|
|
370
370
|
this.config = fillConfig(config);
|
|
371
371
|
this.apiBasePath = this.normalizeApiBasePath(this.config.apiBasePath);
|
|
372
372
|
this.startedAt = Date.now();
|
|
373
|
-
this.storageAdapter = storage_js_1.
|
|
373
|
+
this.storageAdapter = storage_js_1.nullAuthAdapter;
|
|
374
374
|
this.moduleAdapter = module_js_1.nullAuthModule;
|
|
375
375
|
this.jwtHelper = new JwtHelperStore();
|
|
376
376
|
this.tokenStoreAdapter = this.config.tokenStore ?? null;
|
|
@@ -454,7 +454,7 @@ class ApiServer {
|
|
|
454
454
|
}
|
|
455
455
|
return this.oauthStoreAdapter;
|
|
456
456
|
}
|
|
457
|
-
//
|
|
457
|
+
// AuthAdapter-compatible helpers (used by AuthModule)
|
|
458
458
|
async getUser(identifier) {
|
|
459
459
|
return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
|
|
460
460
|
}
|
|
@@ -9,7 +9,7 @@ import { ApiModule } from './api-module.js';
|
|
|
9
9
|
import { TokenStore, type JwtDecodeResult, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
|
|
10
10
|
import type { ApiAuthClass, ApiKey } from './api-module.js';
|
|
11
11
|
import type { AuthProviderModule } from './auth-api/module.js';
|
|
12
|
-
import type {
|
|
12
|
+
import type { AuthAdapter, AuthIdentifier } from './auth-api/types.js';
|
|
13
13
|
import type { OAuthStore } from './oauth/base.js';
|
|
14
14
|
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from './oauth/types.js';
|
|
15
15
|
import type { PasskeyService } from './passkey/service.js';
|
|
@@ -122,17 +122,17 @@ export declare class ApiServer {
|
|
|
122
122
|
private canImpersonateAdapter;
|
|
123
123
|
private readonly jwtHelper;
|
|
124
124
|
constructor(config?: Partial<ApiServerConf>);
|
|
125
|
-
authStorage<UserRow, SafeUser>(storage:
|
|
125
|
+
authStorage<UserRow, SafeUser>(storage: AuthAdapter<UserRow, SafeUser>): this;
|
|
126
126
|
/**
|
|
127
127
|
* @deprecated Use {@link ApiServer.authStorage} instead.
|
|
128
128
|
*/
|
|
129
|
-
useAuthStorage<UserRow, SafeUser>(storage:
|
|
129
|
+
useAuthStorage<UserRow, SafeUser>(storage: AuthAdapter<UserRow, SafeUser>): this;
|
|
130
130
|
authModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
131
131
|
/**
|
|
132
132
|
* @deprecated Use {@link ApiServer.authModule} instead.
|
|
133
133
|
*/
|
|
134
134
|
useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
135
|
-
getAuthStorage():
|
|
135
|
+
getAuthStorage(): AuthAdapter<any, any>;
|
|
136
136
|
getAuthModule(): AuthProviderModule<any>;
|
|
137
137
|
setTokenStore(store: TokenStore): this;
|
|
138
138
|
getTokenStore(): TokenStore | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ApiRequest, type ApiRoute, type ApiServer } from '../api-server-base.js';
|
|
2
2
|
import { BaseAuthModule, type AuthProviderModule } from './module.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
4
4
|
import type { OAuthCallbackParams, OAuthCallbackResult, OAuthStartParams, OAuthStartResult } from '../oauth/types.js';
|
|
5
5
|
import type { TokenPair, Token } from '../token/types.js';
|
|
6
6
|
interface CanImpersonateContext<UserEntity> {
|
|
@@ -46,7 +46,7 @@ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<U
|
|
|
46
46
|
private readonly defaultDomain?;
|
|
47
47
|
private readonly canImpersonateHook?;
|
|
48
48
|
constructor(options?: AuthModuleOptions<UserEntity>);
|
|
49
|
-
protected get storage():
|
|
49
|
+
protected get storage(): AuthAdapter<UserEntity, PublicUser>;
|
|
50
50
|
protected canImpersonate(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<boolean>;
|
|
51
51
|
protected ensureImpersonationAllowed(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<void>;
|
|
52
52
|
protected buildTokenPayload(user: UserEntity, metadata?: TokenMetadata): TokenClaims;
|
|
@@ -57,6 +57,9 @@ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<U
|
|
|
57
57
|
private resolveSessionPreferences;
|
|
58
58
|
private mergeSessionPreferences;
|
|
59
59
|
private sessionPrefsFromRecord;
|
|
60
|
+
private validateCredentialId;
|
|
61
|
+
private normalizeCredentialId;
|
|
62
|
+
private toIsoDate;
|
|
60
63
|
private cookieOptions;
|
|
61
64
|
private setJwtCookies;
|
|
62
65
|
issueTokens(apiReq: ApiRequest, user: UserEntity, metadata?: TokenIssueOptions): Promise<TokenPair>;
|
|
@@ -76,6 +79,8 @@ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<U
|
|
|
76
79
|
private postWhoAmI;
|
|
77
80
|
private postPasskeyChallenge;
|
|
78
81
|
private postPasskeyVerify;
|
|
82
|
+
private getPasskeys;
|
|
83
|
+
private deletePasskey;
|
|
79
84
|
private postImpersonation;
|
|
80
85
|
private deleteImpersonation;
|
|
81
86
|
private getUserFromPasskey;
|
|
@@ -91,6 +96,10 @@ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<U
|
|
|
91
96
|
private resolveClientAuthentication;
|
|
92
97
|
private assertRedirectUriAllowed;
|
|
93
98
|
private resolveUserForOAuth;
|
|
99
|
+
private hasPasskeyService;
|
|
100
|
+
private hasOAuthStore;
|
|
101
|
+
private storageImplements;
|
|
102
|
+
private storageImplementsAll;
|
|
94
103
|
defineRoutes(): ApiRoute[];
|
|
95
104
|
}
|
|
96
105
|
export {};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const node_crypto_1 = require("node:crypto");
|
|
4
|
+
const helpers_1 = require("@simplewebauthn/server/helpers");
|
|
4
5
|
const api_server_base_js_1 = require("../api-server-base.js");
|
|
5
6
|
const module_js_1 = require("./module.js");
|
|
7
|
+
const storage_js_1 = require("./storage.js");
|
|
6
8
|
function isAuthIdentifier(value) {
|
|
7
9
|
return typeof value === 'string' || typeof value === 'number';
|
|
8
10
|
}
|
|
@@ -183,6 +185,44 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
183
185
|
}
|
|
184
186
|
return prefs;
|
|
185
187
|
}
|
|
188
|
+
validateCredentialId(apiReq) {
|
|
189
|
+
const paramId = toStringOrNull(apiReq.req.params?.credentialId);
|
|
190
|
+
const bodyId = toStringOrNull(apiReq.req.body?.credentialId);
|
|
191
|
+
const credentialId = paramId ?? bodyId;
|
|
192
|
+
if (!credentialId) {
|
|
193
|
+
throw new api_server_base_js_1.ApiError({ code: 400, message: 'credentialId is required' });
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
helpers_1.isoBase64URL.toBuffer(credentialId);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
throw new api_server_base_js_1.ApiError({ code: 400, message: 'Invalid credentialId' });
|
|
200
|
+
}
|
|
201
|
+
return credentialId;
|
|
202
|
+
}
|
|
203
|
+
normalizeCredentialId(value) {
|
|
204
|
+
if (Buffer.isBuffer(value)) {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
return Buffer.from(helpers_1.isoBase64URL.toBuffer(value));
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
try {
|
|
212
|
+
return Buffer.from(value, 'base64');
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return Buffer.from(value);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
toIsoDate(value) {
|
|
220
|
+
if (!value) {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
224
|
+
return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
|
|
225
|
+
}
|
|
186
226
|
cookieOptions(apiReq) {
|
|
187
227
|
const conf = this.server.config;
|
|
188
228
|
const forwarded = apiReq.req.headers['x-forwarded-proto'];
|
|
@@ -598,6 +638,44 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
598
638
|
const publicUser = this.storage.filterUser(user);
|
|
599
639
|
return [200, { ...tokens, user: publicUser }];
|
|
600
640
|
}
|
|
641
|
+
async getPasskeys(apiReq) {
|
|
642
|
+
if (typeof this.storage.listUserCredentials !== 'function') {
|
|
643
|
+
throw new api_server_base_js_1.ApiError({ code: 501, message: 'Passkey credential listing is not configured' });
|
|
644
|
+
}
|
|
645
|
+
const { userId } = await this.resolveActorContext(apiReq);
|
|
646
|
+
const credentials = await this.storage.listUserCredentials(userId);
|
|
647
|
+
const safeCredentials = credentials.map((credential) => {
|
|
648
|
+
const bufferId = this.normalizeCredentialId(credential.credentialId);
|
|
649
|
+
return {
|
|
650
|
+
id: helpers_1.isoBase64URL.fromBuffer(new Uint8Array(bufferId)),
|
|
651
|
+
transports: credential.transports,
|
|
652
|
+
backedUp: credential.backedUp,
|
|
653
|
+
deviceType: credential.deviceType,
|
|
654
|
+
createdAt: this.toIsoDate(credential.createdAt),
|
|
655
|
+
updatedAt: this.toIsoDate(credential.updatedAt)
|
|
656
|
+
};
|
|
657
|
+
});
|
|
658
|
+
return [200, { credentials: safeCredentials }];
|
|
659
|
+
}
|
|
660
|
+
async deletePasskey(apiReq) {
|
|
661
|
+
if (typeof this.storage.listUserCredentials !== 'function' ||
|
|
662
|
+
typeof this.storage.deletePasskeyCredential !== 'function') {
|
|
663
|
+
throw new api_server_base_js_1.ApiError({ code: 501, message: 'Passkey credential management is not configured' });
|
|
664
|
+
}
|
|
665
|
+
const { userId } = await this.resolveActorContext(apiReq);
|
|
666
|
+
const credentialId = this.validateCredentialId(apiReq);
|
|
667
|
+
const bufferId = Buffer.from(helpers_1.isoBase64URL.toBuffer(credentialId));
|
|
668
|
+
const credentials = await this.storage.listUserCredentials(userId);
|
|
669
|
+
const owns = credentials.some((credential) => {
|
|
670
|
+
const candidateId = this.normalizeCredentialId(credential.credentialId);
|
|
671
|
+
return helpers_1.isoBase64URL.fromBuffer(new Uint8Array(candidateId)) === credentialId;
|
|
672
|
+
});
|
|
673
|
+
if (!owns) {
|
|
674
|
+
throw new api_server_base_js_1.ApiError({ code: 404, message: 'Passkey not found' });
|
|
675
|
+
}
|
|
676
|
+
const deleted = await this.storage.deletePasskeyCredential(bufferId);
|
|
677
|
+
return [200, { deleted }];
|
|
678
|
+
}
|
|
601
679
|
async postImpersonation(apiReq) {
|
|
602
680
|
this.assertAuthReady();
|
|
603
681
|
const { targetIdentifier, metadata } = this.parseImpersonationRequest(apiReq);
|
|
@@ -941,47 +1019,91 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
941
1019
|
}
|
|
942
1020
|
throw new api_server_base_js_1.ApiError({ code: 401, message: 'Authorization requires user authentication' });
|
|
943
1021
|
}
|
|
1022
|
+
hasPasskeyService() {
|
|
1023
|
+
const storageAny = this.storage;
|
|
1024
|
+
if (storageAny.passkeyService || storageAny.passkeyStore) {
|
|
1025
|
+
return true;
|
|
1026
|
+
}
|
|
1027
|
+
if (storageAny.adapter?.passkeyService || storageAny.adapter?.passkeyStore) {
|
|
1028
|
+
return true;
|
|
1029
|
+
}
|
|
1030
|
+
const serverAny = this.server;
|
|
1031
|
+
return !!serverAny.passkeyServiceAdapter;
|
|
1032
|
+
}
|
|
1033
|
+
hasOAuthStore() {
|
|
1034
|
+
const storageAny = this.storage;
|
|
1035
|
+
if (storageAny.oauthStore) {
|
|
1036
|
+
return true;
|
|
1037
|
+
}
|
|
1038
|
+
if (storageAny.adapter?.oauthStore) {
|
|
1039
|
+
return true;
|
|
1040
|
+
}
|
|
1041
|
+
const serverAny = this.server;
|
|
1042
|
+
return !!serverAny.oauthStoreAdapter;
|
|
1043
|
+
}
|
|
1044
|
+
storageImplements(key) {
|
|
1045
|
+
const candidate = this.storage[key];
|
|
1046
|
+
if (typeof candidate !== 'function') {
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
const baseImpl = storage_js_1.BaseAuthAdapter.prototype[key];
|
|
1050
|
+
return candidate !== baseImpl;
|
|
1051
|
+
}
|
|
1052
|
+
storageImplementsAll(keys) {
|
|
1053
|
+
return keys.every((key) => this.storageImplements(key));
|
|
1054
|
+
}
|
|
944
1055
|
defineRoutes() {
|
|
945
|
-
const routes = [
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1056
|
+
const routes = [];
|
|
1057
|
+
const coreAuthSupported = this.storageImplementsAll([
|
|
1058
|
+
'getUser',
|
|
1059
|
+
'getUserPasswordHash',
|
|
1060
|
+
'getUserId',
|
|
1061
|
+
'verifyPassword',
|
|
1062
|
+
'filterUser',
|
|
1063
|
+
'storeToken',
|
|
1064
|
+
'getToken',
|
|
1065
|
+
'deleteToken'
|
|
1066
|
+
]);
|
|
1067
|
+
if (!coreAuthSupported) {
|
|
1068
|
+
return routes;
|
|
1069
|
+
}
|
|
1070
|
+
routes.push({
|
|
1071
|
+
method: 'post',
|
|
1072
|
+
path: '/v1/login',
|
|
1073
|
+
handler: (req) => this.postLogin(req),
|
|
1074
|
+
auth: { type: 'none', req: 'any' }
|
|
1075
|
+
}, {
|
|
1076
|
+
method: 'post',
|
|
1077
|
+
path: '/v1/refresh',
|
|
1078
|
+
handler: (req) => this.postRefresh(req),
|
|
1079
|
+
auth: { type: 'none', req: 'any' }
|
|
1080
|
+
}, {
|
|
1081
|
+
method: 'post',
|
|
1082
|
+
path: '/v1/logout',
|
|
1083
|
+
handler: (req) => this.postLogout(req),
|
|
1084
|
+
auth: { type: 'maybe', req: 'any' }
|
|
1085
|
+
}, {
|
|
1086
|
+
method: 'post',
|
|
1087
|
+
path: '/v1/whoami',
|
|
1088
|
+
handler: (req) => this.postWhoAmI(req),
|
|
1089
|
+
auth: { type: 'maybe', req: 'any' }
|
|
1090
|
+
}, {
|
|
1091
|
+
method: 'post',
|
|
1092
|
+
path: '/v1/impersonations',
|
|
1093
|
+
handler: (req) => this.postImpersonation(req),
|
|
1094
|
+
auth: { type: 'strict', req: 'any' }
|
|
1095
|
+
}, {
|
|
1096
|
+
method: 'delete',
|
|
1097
|
+
path: '/v1/impersonations',
|
|
1098
|
+
handler: (req) => this.deleteImpersonation(req),
|
|
1099
|
+
auth: { type: 'strict', req: 'any' }
|
|
1100
|
+
});
|
|
1101
|
+
const passkeysSupported = this.hasPasskeyService() &&
|
|
1102
|
+
this.storageImplements('createPasskeyChallenge') &&
|
|
1103
|
+
this.storageImplements('verifyPasskeyResponse');
|
|
1104
|
+
const passkeyCredentialsSupported = passkeysSupported &&
|
|
1105
|
+
this.storageImplements('listUserCredentials') &&
|
|
1106
|
+
this.storageImplements('deletePasskeyCredential');
|
|
985
1107
|
if (passkeysSupported) {
|
|
986
1108
|
routes.push({
|
|
987
1109
|
method: 'post',
|
|
@@ -994,6 +1116,19 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
994
1116
|
handler: (req) => this.postPasskeyVerify(req),
|
|
995
1117
|
auth: { type: 'none', req: 'any' }
|
|
996
1118
|
});
|
|
1119
|
+
if (passkeyCredentialsSupported) {
|
|
1120
|
+
routes.push({
|
|
1121
|
+
method: 'get',
|
|
1122
|
+
path: '/v1/passkeys',
|
|
1123
|
+
handler: (req) => this.getPasskeys(req),
|
|
1124
|
+
auth: { type: 'strict', req: 'any' }
|
|
1125
|
+
}, {
|
|
1126
|
+
method: 'delete',
|
|
1127
|
+
path: '/v1/passkeys/:credentialId?',
|
|
1128
|
+
handler: (req) => this.deletePasskey(req),
|
|
1129
|
+
auth: { type: 'strict', req: 'any' }
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
997
1132
|
}
|
|
998
1133
|
const externalOAuthSupported = typeof this.server.initiateOAuth === 'function' && typeof this.server.completeOAuth === 'function';
|
|
999
1134
|
if (externalOAuthSupported) {
|
|
@@ -1009,9 +1144,10 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
1009
1144
|
auth: { type: 'none', req: 'any' }
|
|
1010
1145
|
});
|
|
1011
1146
|
}
|
|
1012
|
-
const oauthStorageSupported =
|
|
1013
|
-
|
|
1014
|
-
|
|
1147
|
+
const oauthStorageSupported = this.hasOAuthStore() &&
|
|
1148
|
+
this.storageImplements('getClient') &&
|
|
1149
|
+
this.storageImplements('createAuthCode') &&
|
|
1150
|
+
this.storageImplements('consumeAuthCode');
|
|
1015
1151
|
if (oauthStorageSupported) {
|
|
1016
1152
|
routes.push({
|
|
1017
1153
|
method: 'post',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { PasskeyService } from '../passkey/service.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
3
3
|
import type { OAuthStore } from '../oauth/base.js';
|
|
4
4
|
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
5
5
|
import type { PasskeyStore } from '../passkey/base.js';
|
|
6
|
-
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
6
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
7
7
|
import type { TokenStore } from '../token/base.js';
|
|
8
8
|
import type { Token } from '../token/types.js';
|
|
9
9
|
import type { UserStore } from '../user/base.js';
|
|
@@ -11,7 +11,7 @@ interface PasskeyAdapterOptions {
|
|
|
11
11
|
store: PasskeyStore;
|
|
12
12
|
config: PasskeyServiceConfig;
|
|
13
13
|
}
|
|
14
|
-
export interface
|
|
14
|
+
export interface AuthAdapterOptions<UserRow, PublicUser> {
|
|
15
15
|
userStore: UserStore<UserRow, PublicUser>;
|
|
16
16
|
tokenStore: TokenStore;
|
|
17
17
|
passkeys?: PasskeyAdapterOptions | PasskeyService;
|
|
@@ -21,13 +21,13 @@ export interface AuthStorageAdapterOptions<UserRow, PublicUser> {
|
|
|
21
21
|
effectiveUserId: AuthIdentifier;
|
|
22
22
|
}) => boolean | Promise<boolean>;
|
|
23
23
|
}
|
|
24
|
-
export declare class
|
|
24
|
+
export declare class CompositeAuthAdapter<UserRow, PublicUser> implements AuthAdapter<UserRow, PublicUser> {
|
|
25
25
|
private readonly userStore;
|
|
26
26
|
private readonly tokenStore;
|
|
27
27
|
private readonly oauthStore?;
|
|
28
28
|
private readonly passkeyService?;
|
|
29
29
|
private readonly canImpersonateFn?;
|
|
30
|
-
constructor(options:
|
|
30
|
+
constructor(options: AuthAdapterOptions<UserRow, PublicUser>);
|
|
31
31
|
getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
|
|
32
32
|
getUserPasswordHash(user: UserRow): string;
|
|
33
33
|
getUserId(user: UserRow): AuthIdentifier;
|
|
@@ -43,6 +43,8 @@ export declare class AuthStorageAdapter<UserRow, PublicUser> implements AuthStor
|
|
|
43
43
|
}): Promise<boolean>;
|
|
44
44
|
createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
45
45
|
verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
46
|
+
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
47
|
+
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
46
48
|
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
47
49
|
verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
48
50
|
createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.CompositeAuthAdapter = void 0;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
const service_js_1 = require("../passkey/service.js");
|
|
6
|
-
class
|
|
6
|
+
class CompositeAuthAdapter {
|
|
7
7
|
constructor(options) {
|
|
8
8
|
this.userStore = options.userStore;
|
|
9
9
|
this.tokenStore = options.tokenStore;
|
|
@@ -55,6 +55,18 @@ class AuthStorageAdapter {
|
|
|
55
55
|
}
|
|
56
56
|
return this.passkeyService.verifyResponse(params);
|
|
57
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
|
+
}
|
|
58
70
|
async getClient(clientId) {
|
|
59
71
|
if (!this.oauthStore) {
|
|
60
72
|
return null;
|
|
@@ -113,4 +125,4 @@ class AuthStorageAdapter {
|
|
|
113
125
|
return params.realUserId === params.effectiveUserId;
|
|
114
126
|
}
|
|
115
127
|
}
|
|
116
|
-
exports.
|
|
128
|
+
exports.CompositeAuthAdapter = CompositeAuthAdapter;
|
|
@@ -2,11 +2,11 @@ import { MemoryOAuthStore } from '../oauth/memory.js';
|
|
|
2
2
|
import { MemoryPasskeyStore } from '../passkey/memory.js';
|
|
3
3
|
import { TokenStore } from '../token/base.js';
|
|
4
4
|
import { MemoryUserStore } from '../user/memory.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
6
6
|
import type { MemoryOAuthStoreOptions } from '../oauth/memory.js';
|
|
7
7
|
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
8
8
|
import type { MemoryPasskeyStoreOptions } from '../passkey/memory.js';
|
|
9
|
-
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
9
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
10
10
|
import type { Token } from '../token/types.js';
|
|
11
11
|
import type { MemoryUserAttributes, MemoryPublicUser, MemoryUserStoreOptions } from '../user/memory.js';
|
|
12
12
|
interface PasskeyOptions extends Partial<PasskeyServiceConfig> {
|
|
@@ -24,7 +24,7 @@ export interface MemAuthStoreParams<UserAttributes extends MemoryUserAttributes
|
|
|
24
24
|
}) => boolean | Promise<boolean>;
|
|
25
25
|
tokenStore?: TokenStore;
|
|
26
26
|
}
|
|
27
|
-
export declare class MemAuthStore<UserAttributes extends MemoryUserAttributes = MemoryUserAttributes, PublicUserShape extends MemoryPublicUser<UserAttributes> = MemoryPublicUser<UserAttributes>> implements
|
|
27
|
+
export declare class MemAuthStore<UserAttributes extends MemoryUserAttributes = MemoryUserAttributes, PublicUserShape extends MemoryPublicUser<UserAttributes> = MemoryPublicUser<UserAttributes>> implements AuthAdapter<UserAttributes, PublicUserShape> {
|
|
28
28
|
readonly userStore: MemoryUserStore<UserAttributes, PublicUserShape>;
|
|
29
29
|
readonly tokenStore: TokenStore;
|
|
30
30
|
readonly passkeyStore?: MemoryPasskeyStore;
|
|
@@ -54,6 +54,8 @@ export declare class MemAuthStore<UserAttributes extends MemoryUserAttributes =
|
|
|
54
54
|
}): Promise<boolean>;
|
|
55
55
|
createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
56
56
|
verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
57
|
+
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
58
|
+
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
57
59
|
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
58
60
|
verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
59
61
|
createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
@@ -59,7 +59,7 @@ class MemAuthStore {
|
|
|
59
59
|
passkeyStore = new memory_js_2.MemoryPasskeyStore({ resolveUser });
|
|
60
60
|
this.passkeyStore = passkeyStore;
|
|
61
61
|
}
|
|
62
|
-
this.adapter = new compat_auth_storage_js_1.
|
|
62
|
+
this.adapter = new compat_auth_storage_js_1.CompositeAuthAdapter({
|
|
63
63
|
userStore: this.userStore,
|
|
64
64
|
tokenStore: this.tokenStore,
|
|
65
65
|
passkeys: passkeyStore && passkeyConfig ? { store: passkeyStore, config: passkeyConfig } : undefined,
|
|
@@ -116,6 +116,12 @@ class MemAuthStore {
|
|
|
116
116
|
async verifyPasskeyResponse(params) {
|
|
117
117
|
return this.adapter.verifyPasskeyResponse(params);
|
|
118
118
|
}
|
|
119
|
+
async listUserCredentials(userId) {
|
|
120
|
+
return this.adapter.listUserCredentials(userId);
|
|
121
|
+
}
|
|
122
|
+
async deletePasskeyCredential(credentialId) {
|
|
123
|
+
return this.adapter.deletePasskeyCredential(credentialId);
|
|
124
|
+
}
|
|
119
125
|
async getClient(clientId) {
|
|
120
126
|
return this.adapter.getClient(clientId);
|
|
121
127
|
}
|
|
@@ -3,9 +3,9 @@ import { SequelizeOAuthStore, type SequelizeOAuthStoreOptions } from '../oauth/s
|
|
|
3
3
|
import { SequelizePasskeyStore } from '../passkey/sequelize.js';
|
|
4
4
|
import { type SequelizeTokenStoreOptions } from '../token/sequelize.js';
|
|
5
5
|
import { SequelizeUserStore, type AuthUserAttributes, GenericUserModel, GenericUserModelStatic } from '../user/sequelize.js';
|
|
6
|
-
import type {
|
|
6
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
7
7
|
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
8
|
-
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
8
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
9
9
|
import type { TokenStore } from '../token/base.js';
|
|
10
10
|
import type { Token } from '../token/types.js';
|
|
11
11
|
interface PasskeyOptions extends Partial<PasskeyServiceConfig> {
|
|
@@ -30,7 +30,7 @@ export interface SqlAuthStoreParams<UserAttributes extends AuthUserAttributes =
|
|
|
30
30
|
tokenStoreOptions?: Omit<SequelizeTokenStoreOptions, 'sequelize'>;
|
|
31
31
|
oauthStoreOptions?: Omit<SequelizeOAuthStoreOptions, 'sequelize'>;
|
|
32
32
|
}
|
|
33
|
-
export declare class SqlAuthStore<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> implements
|
|
33
|
+
export declare class SqlAuthStore<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> implements AuthAdapter<UserAttributes, PublicUserShape> {
|
|
34
34
|
readonly userStore: SequelizeUserStore<UserAttributes, PublicUserShape>;
|
|
35
35
|
readonly tokenStore: TokenStore;
|
|
36
36
|
readonly passkeyStore?: SequelizePasskeyStore;
|
|
@@ -63,6 +63,8 @@ export declare class SqlAuthStore<UserAttributes extends AuthUserAttributes = Au
|
|
|
63
63
|
}): Promise<boolean>;
|
|
64
64
|
createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
65
65
|
verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
66
|
+
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
67
|
+
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
66
68
|
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
67
69
|
verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
68
70
|
createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
@@ -72,7 +72,7 @@ class SqlAuthStore {
|
|
|
72
72
|
passkeyStore = new sequelize_js_2.SequelizePasskeyStore({ sequelize: this.sequelize, resolveUser });
|
|
73
73
|
this.passkeyStore = passkeyStore;
|
|
74
74
|
}
|
|
75
|
-
this.adapter = new compat_auth_storage_js_1.
|
|
75
|
+
this.adapter = new compat_auth_storage_js_1.CompositeAuthAdapter({
|
|
76
76
|
userStore: this.userStore,
|
|
77
77
|
tokenStore: this.tokenStore,
|
|
78
78
|
passkeys: passkeyStore && passkeyConfig ? { store: passkeyStore, config: passkeyConfig } : undefined,
|
|
@@ -147,6 +147,12 @@ class SqlAuthStore {
|
|
|
147
147
|
async verifyPasskeyResponse(params) {
|
|
148
148
|
return this.adapter.verifyPasskeyResponse(params);
|
|
149
149
|
}
|
|
150
|
+
async listUserCredentials(userId) {
|
|
151
|
+
return this.adapter.listUserCredentials(userId);
|
|
152
|
+
}
|
|
153
|
+
async deletePasskeyCredential(credentialId) {
|
|
154
|
+
return this.adapter.deletePasskeyCredential(credentialId);
|
|
155
|
+
}
|
|
150
156
|
async getClient(clientId) {
|
|
151
157
|
return this.adapter.getClient(clientId);
|
|
152
158
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
2
2
|
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
3
|
-
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
3
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult, StoredPasskeyCredential } from '../passkey/types.js';
|
|
4
4
|
import type { Token } from '../token/types.js';
|
|
5
|
-
export declare class
|
|
5
|
+
export declare class BaseAuthAdapter<UserRow = unknown, SafeUser = unknown> implements AuthAdapter<UserRow, SafeUser> {
|
|
6
6
|
getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
|
|
7
7
|
getUserPasswordHash(user: UserRow): string;
|
|
8
8
|
getUserId(user: UserRow): AuthIdentifier;
|
|
@@ -24,6 +24,8 @@ export declare class BaseAuthStorage<UserRow = unknown, SafeUser = unknown> impl
|
|
|
24
24
|
}): Promise<boolean>;
|
|
25
25
|
createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
26
26
|
verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
27
|
+
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
28
|
+
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
27
29
|
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
28
30
|
verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
29
31
|
createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
@@ -33,4 +35,4 @@ export declare class BaseAuthStorage<UserRow = unknown, SafeUser = unknown> impl
|
|
|
33
35
|
effectiveUserId: AuthIdentifier;
|
|
34
36
|
}): Promise<boolean>;
|
|
35
37
|
}
|
|
36
|
-
export declare const
|
|
38
|
+
export declare const nullAuthAdapter: AuthAdapter<unknown, unknown>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
// Handy base you can extend when wiring a real
|
|
3
|
+
exports.nullAuthAdapter = exports.BaseAuthAdapter = void 0;
|
|
4
|
+
// Handy base you can extend when wiring a real auth adapter. Every method
|
|
5
5
|
// throws by default so unimplemented hooks fail loudly.
|
|
6
|
-
class
|
|
6
|
+
class BaseAuthAdapter {
|
|
7
7
|
// Override to load a user record by identifier
|
|
8
8
|
async getUser(identifier) {
|
|
9
9
|
void identifier;
|
|
@@ -60,6 +60,16 @@ class BaseAuthStorage {
|
|
|
60
60
|
void params;
|
|
61
61
|
throw new Error('Auth storage not configured');
|
|
62
62
|
}
|
|
63
|
+
// Override to list passkey credentials for a user
|
|
64
|
+
async listUserCredentials(userId) {
|
|
65
|
+
void userId;
|
|
66
|
+
throw new Error('Auth storage not configured');
|
|
67
|
+
}
|
|
68
|
+
// Override to delete a passkey credential
|
|
69
|
+
async deletePasskeyCredential(credentialId) {
|
|
70
|
+
void credentialId;
|
|
71
|
+
throw new Error('Auth storage not configured');
|
|
72
|
+
}
|
|
63
73
|
// Override to fetch an OAuth client by identifier
|
|
64
74
|
async getClient(clientId) {
|
|
65
75
|
void clientId;
|
|
@@ -88,5 +98,5 @@ class BaseAuthStorage {
|
|
|
88
98
|
return false;
|
|
89
99
|
}
|
|
90
100
|
}
|
|
91
|
-
exports.
|
|
92
|
-
exports.
|
|
101
|
+
exports.BaseAuthAdapter = BaseAuthAdapter;
|
|
102
|
+
exports.nullAuthAdapter = new BaseAuthAdapter();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
2
|
-
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
|
|
2
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult, StoredPasskeyCredential } from '../passkey/types.js';
|
|
3
3
|
import type { Token } from '../token/types.js';
|
|
4
4
|
export type AuthIdentifier = string | number;
|
|
5
|
-
|
|
5
|
+
/** @internal */
|
|
6
|
+
export interface AuthAdapter<UserRow, SafeUser> {
|
|
6
7
|
getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
|
|
7
8
|
getUserPasswordHash(user: UserRow): string;
|
|
8
9
|
getUserId(user: UserRow): AuthIdentifier;
|
|
@@ -18,6 +19,8 @@ export interface AuthStorage<UserRow, SafeUser> {
|
|
|
18
19
|
}): Promise<boolean>;
|
|
19
20
|
createPasskeyChallenge?(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
20
21
|
verifyPasskeyResponse?(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
22
|
+
listUserCredentials?(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
23
|
+
deletePasskeyCredential?(credentialId: Buffer | string): Promise<boolean>;
|
|
21
24
|
getClient?(clientId: string): Promise<OAuthClient | null>;
|
|
22
25
|
verifyClientSecret?(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
23
26
|
createAuthCode?(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
@@ -27,3 +30,5 @@ export interface AuthStorage<UserRow, SafeUser> {
|
|
|
27
30
|
effectiveUserId: AuthIdentifier;
|
|
28
31
|
}): Promise<boolean>;
|
|
29
32
|
}
|
|
33
|
+
/** @internal */
|
|
34
|
+
export type AuthStorage<UserRow, SafeUser> = AuthAdapter<UserRow, SafeUser>;
|