@technomoron/api-server-base 2.0.0-beta.1 → 2.0.0-beta.10
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/README.txt +25 -2
- package/dist/cjs/api-server-base.cjs +269 -39
- package/dist/cjs/api-server-base.d.ts +27 -7
- package/dist/cjs/auth-api/auth-module.d.ts +11 -2
- package/dist/cjs/auth-api/auth-module.js +193 -45
- 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 +4 -4
- package/dist/cjs/oauth/sequelize.js +1 -1
- 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 +145 -10
- package/dist/cjs/passkey/types.d.ts +3 -0
- package/dist/cjs/user/base.js +2 -1
- package/dist/esm/api-server-base.d.ts +27 -7
- package/dist/esm/api-server-base.js +270 -40
- package/dist/esm/auth-api/auth-module.d.ts +11 -2
- package/dist/esm/auth-api/auth-module.js +194 -46
- 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 +4 -4
- package/dist/esm/index.js +2 -2
- package/dist/esm/oauth/sequelize.js +1 -1
- 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 +113 -11
- package/dist/esm/passkey/types.d.ts +3 -0
- package/dist/esm/user/base.js +2 -1
- package/package.json +3 -1
|
@@ -10,7 +10,7 @@ import cors from 'cors';
|
|
|
10
10
|
import express from 'express';
|
|
11
11
|
import multer from 'multer';
|
|
12
12
|
import { nullAuthModule } from './auth-api/module.js';
|
|
13
|
-
import {
|
|
13
|
+
import { nullAuthAdapter } from './auth-api/storage.js';
|
|
14
14
|
import { TokenStore } from './token/base.js';
|
|
15
15
|
class JwtHelperStore extends TokenStore {
|
|
16
16
|
async save() {
|
|
@@ -344,6 +344,7 @@ function fillConfig(config) {
|
|
|
344
344
|
devMode: config.devMode ?? false,
|
|
345
345
|
hydrateGetBody: config.hydrateGetBody ?? true,
|
|
346
346
|
validateTokens: config.validateTokens ?? false,
|
|
347
|
+
refreshMaybe: config.refreshMaybe ?? false,
|
|
347
348
|
apiVersion: config.apiVersion ?? '',
|
|
348
349
|
minClientVersion: config.minClientVersion ?? '',
|
|
349
350
|
tokenStore: config.tokenStore,
|
|
@@ -362,7 +363,7 @@ export class ApiServer {
|
|
|
362
363
|
this.config = fillConfig(config);
|
|
363
364
|
this.apiBasePath = this.normalizeApiBasePath(this.config.apiBasePath);
|
|
364
365
|
this.startedAt = Date.now();
|
|
365
|
-
this.storageAdapter =
|
|
366
|
+
this.storageAdapter = nullAuthAdapter;
|
|
366
367
|
this.moduleAdapter = nullAuthModule;
|
|
367
368
|
this.jwtHelper = new JwtHelperStore();
|
|
368
369
|
this.tokenStoreAdapter = this.config.tokenStore ?? null;
|
|
@@ -440,13 +441,19 @@ export class ApiServer {
|
|
|
440
441
|
}
|
|
441
442
|
return this.passkeyServiceAdapter;
|
|
442
443
|
}
|
|
444
|
+
async listUserCredentials(userId) {
|
|
445
|
+
return this.ensurePasskeyService().listUserCredentials(userId);
|
|
446
|
+
}
|
|
447
|
+
async deletePasskeyCredential(credentialId) {
|
|
448
|
+
return this.ensurePasskeyService().deleteCredential(credentialId);
|
|
449
|
+
}
|
|
443
450
|
ensureOAuthStore() {
|
|
444
451
|
if (!this.oauthStoreAdapter) {
|
|
445
452
|
throw new Error('OAuth store is not configured');
|
|
446
453
|
}
|
|
447
454
|
return this.oauthStoreAdapter;
|
|
448
455
|
}
|
|
449
|
-
//
|
|
456
|
+
// AuthAdapter-compatible helpers (used by AuthModule)
|
|
450
457
|
async getUser(identifier) {
|
|
451
458
|
return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
|
|
452
459
|
}
|
|
@@ -692,16 +699,117 @@ export class ApiServer {
|
|
|
692
699
|
}
|
|
693
700
|
async verifyJWT(token) {
|
|
694
701
|
if (!this.config.accessSecret) {
|
|
695
|
-
return { tokenData: undefined, error: 'JWT authentication disabled; no jwtSecret set' };
|
|
702
|
+
return { tokenData: undefined, error: 'JWT authentication disabled; no jwtSecret set', expired: false };
|
|
696
703
|
}
|
|
697
704
|
const result = this.jwtVerify(token, this.config.accessSecret);
|
|
698
705
|
if (!result.success) {
|
|
699
|
-
return { tokenData: undefined, error: result.error };
|
|
706
|
+
return { tokenData: undefined, error: result.error, expired: result.expired };
|
|
700
707
|
}
|
|
701
708
|
if (!result.data.uid) {
|
|
702
|
-
return { tokenData: undefined, error: 'Missing/bad userid in token' };
|
|
709
|
+
return { tokenData: undefined, error: 'Missing/bad userid in token', expired: false };
|
|
710
|
+
}
|
|
711
|
+
return { tokenData: result.data, error: undefined, expired: false };
|
|
712
|
+
}
|
|
713
|
+
jwtCookieOptions(apiReq) {
|
|
714
|
+
const conf = this.config;
|
|
715
|
+
const forwarded = apiReq.req.headers['x-forwarded-proto'];
|
|
716
|
+
const referer = apiReq.req.headers['origin'] ?? apiReq.req.headers['referer'];
|
|
717
|
+
const origin = typeof referer === 'string' ? referer : '';
|
|
718
|
+
const isHttps = forwarded === 'https' || apiReq.req.protocol === 'https';
|
|
719
|
+
const isLocalhost = origin.includes('localhost');
|
|
720
|
+
const options = {
|
|
721
|
+
httpOnly: true,
|
|
722
|
+
secure: true,
|
|
723
|
+
sameSite: 'strict',
|
|
724
|
+
domain: conf.cookieDomain || undefined,
|
|
725
|
+
path: '/',
|
|
726
|
+
maxAge: undefined
|
|
727
|
+
};
|
|
728
|
+
if (conf.devMode) {
|
|
729
|
+
options.secure = isHttps;
|
|
730
|
+
options.httpOnly = false;
|
|
731
|
+
options.sameSite = 'lax';
|
|
732
|
+
if (isLocalhost) {
|
|
733
|
+
options.domain = undefined;
|
|
734
|
+
}
|
|
703
735
|
}
|
|
704
|
-
return
|
|
736
|
+
return options;
|
|
737
|
+
}
|
|
738
|
+
setAccessCookie(apiReq, accessToken, sessionCookie) {
|
|
739
|
+
const conf = this.config;
|
|
740
|
+
const options = this.jwtCookieOptions(apiReq);
|
|
741
|
+
const accessMaxAge = Math.max(1, conf.accessExpiry) * 1000;
|
|
742
|
+
const accessOptions = sessionCookie ? options : { ...options, maxAge: accessMaxAge };
|
|
743
|
+
apiReq.res.cookie(conf.accessCookie, accessToken, accessOptions);
|
|
744
|
+
}
|
|
745
|
+
async tryRefreshAccessToken(apiReq) {
|
|
746
|
+
const conf = this.config;
|
|
747
|
+
if (!conf.refreshSecret || !conf.accessSecret) {
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
const rawRefresh = apiReq.req.cookies?.[conf.refreshCookie];
|
|
751
|
+
if (typeof rawRefresh !== 'string') {
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
const refreshToken = rawRefresh.trim();
|
|
755
|
+
if (!refreshToken) {
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
const verify = this.jwtVerify(refreshToken, conf.refreshSecret);
|
|
759
|
+
if (!verify.success || !verify.data) {
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
let stored = null;
|
|
763
|
+
try {
|
|
764
|
+
stored = await this.storageAdapter.getToken({ refreshToken });
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
if (!stored) {
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
const storedUid = String(stored.userId);
|
|
773
|
+
const verifyUid = verify.data.uid === undefined || verify.data.uid === null ? null : String(verify.data.uid);
|
|
774
|
+
if (verifyUid && verifyUid !== storedUid) {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
const claims = verify.data;
|
|
778
|
+
const { exp: _exp, iat: _iat, nbf: _nbf, ...payload } = claims;
|
|
779
|
+
void _exp;
|
|
780
|
+
void _iat;
|
|
781
|
+
void _nbf;
|
|
782
|
+
// Ensure we never embed token secrets into refreshed access tokens.
|
|
783
|
+
delete payload.accessToken;
|
|
784
|
+
delete payload.refreshToken;
|
|
785
|
+
delete payload.userId;
|
|
786
|
+
delete payload.expires;
|
|
787
|
+
delete payload.issuedAt;
|
|
788
|
+
delete payload.lastSeenAt;
|
|
789
|
+
delete payload.status;
|
|
790
|
+
const access = this.jwtSign(payload, conf.accessSecret, conf.accessExpiry);
|
|
791
|
+
if (!access.success || !access.token) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
const updated = await this.updateToken({
|
|
795
|
+
refreshToken,
|
|
796
|
+
accessToken: access.token,
|
|
797
|
+
lastSeenAt: new Date()
|
|
798
|
+
});
|
|
799
|
+
if (!updated) {
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
this.setAccessCookie(apiReq, access.token, stored.sessionCookie ?? false);
|
|
803
|
+
if (apiReq.req.cookies) {
|
|
804
|
+
apiReq.req.cookies[conf.accessCookie] = access.token;
|
|
805
|
+
}
|
|
806
|
+
const verifiedAccess = await this.verifyJWT(access.token);
|
|
807
|
+
if (!verifiedAccess.tokenData) {
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
const refreshedStored = { ...stored, accessToken: access.token };
|
|
811
|
+
apiReq.authToken = refreshedStored;
|
|
812
|
+
return { token: access.token, tokenData: verifiedAccess.tokenData, stored: refreshedStored };
|
|
705
813
|
}
|
|
706
814
|
async authenticate(apiReq, authType) {
|
|
707
815
|
if (authType === 'none') {
|
|
@@ -711,6 +819,7 @@ export class ApiServer {
|
|
|
711
819
|
let token = null;
|
|
712
820
|
const authHeader = apiReq.req.headers.authorization;
|
|
713
821
|
const requiresAuthToken = this.requiresAuthToken(authType);
|
|
822
|
+
const allowRefresh = requiresAuthToken || (authType === 'maybe' && this.config.refreshMaybe);
|
|
714
823
|
const apiKeyAuth = await this.tryAuthenticateApiKey(apiReq, authType, authHeader);
|
|
715
824
|
if (apiKeyAuth) {
|
|
716
825
|
return apiKeyAuth;
|
|
@@ -719,32 +828,84 @@ export class ApiServer {
|
|
|
719
828
|
token = authHeader.slice(7).trim();
|
|
720
829
|
}
|
|
721
830
|
if (!token) {
|
|
722
|
-
const access = apiReq.req.cookies?.
|
|
831
|
+
const access = apiReq.req.cookies?.[this.config.accessCookie];
|
|
723
832
|
if (access) {
|
|
724
833
|
token = access;
|
|
725
834
|
}
|
|
726
835
|
}
|
|
836
|
+
let tokenData;
|
|
837
|
+
let error;
|
|
838
|
+
let expired = false;
|
|
727
839
|
if (!token || token === null) {
|
|
728
|
-
if (
|
|
729
|
-
|
|
840
|
+
if (authType === 'maybe') {
|
|
841
|
+
if (!this.config.refreshMaybe) {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
const refreshed = await this.tryRefreshAccessToken(apiReq);
|
|
845
|
+
if (!refreshed) {
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
token = refreshed.token;
|
|
849
|
+
tokenData = refreshed.tokenData;
|
|
850
|
+
error = undefined;
|
|
851
|
+
expired = false;
|
|
852
|
+
}
|
|
853
|
+
else if (requiresAuthToken) {
|
|
854
|
+
const refreshed = await this.tryRefreshAccessToken(apiReq);
|
|
855
|
+
if (!refreshed) {
|
|
856
|
+
throw new ApiError({ code: 401, message: 'Authorization token is required (Bearer/cookie)' });
|
|
857
|
+
}
|
|
858
|
+
token = refreshed.token;
|
|
859
|
+
tokenData = refreshed.tokenData;
|
|
860
|
+
error = undefined;
|
|
861
|
+
expired = false;
|
|
730
862
|
}
|
|
731
863
|
}
|
|
732
864
|
if (!token) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
865
|
+
throw new ApiError({ code: 401, message: 'Unauthorized Access - requires authentication' });
|
|
866
|
+
}
|
|
867
|
+
if (!tokenData) {
|
|
868
|
+
const verified = await this.verifyJWT(token);
|
|
869
|
+
tokenData = verified.tokenData;
|
|
870
|
+
error = verified.error;
|
|
871
|
+
expired = verified.expired ?? false;
|
|
872
|
+
}
|
|
873
|
+
if (!tokenData && allowRefresh && expired) {
|
|
874
|
+
const refreshed = await this.tryRefreshAccessToken(apiReq);
|
|
875
|
+
if (refreshed) {
|
|
876
|
+
token = refreshed.token;
|
|
877
|
+
tokenData = refreshed.tokenData;
|
|
878
|
+
error = undefined;
|
|
738
879
|
}
|
|
739
880
|
}
|
|
740
|
-
const { tokenData, error } = await this.verifyJWT(token);
|
|
741
881
|
if (!tokenData) {
|
|
742
882
|
throw new ApiError({ code: 401, message: 'Unathorized Access - ' + error });
|
|
743
883
|
}
|
|
744
884
|
const effectiveUserId = this.extractTokenUserId(tokenData);
|
|
745
885
|
apiReq.realUid = this.resolveRealUserId(tokenData, effectiveUserId);
|
|
746
886
|
if (this.shouldValidateStoredToken(authType)) {
|
|
747
|
-
|
|
887
|
+
try {
|
|
888
|
+
await this.assertStoredAccessToken(apiReq, token, tokenData);
|
|
889
|
+
}
|
|
890
|
+
catch (error) {
|
|
891
|
+
if (allowRefresh &&
|
|
892
|
+
error instanceof ApiError &&
|
|
893
|
+
error.code === 401 &&
|
|
894
|
+
error.message === 'Authorization token is no longer valid') {
|
|
895
|
+
const refreshed = await this.tryRefreshAccessToken(apiReq);
|
|
896
|
+
if (!refreshed) {
|
|
897
|
+
throw error;
|
|
898
|
+
}
|
|
899
|
+
token = refreshed.token;
|
|
900
|
+
tokenData = refreshed.tokenData;
|
|
901
|
+
const refreshedUserId = this.extractTokenUserId(tokenData);
|
|
902
|
+
apiReq.realUid = this.resolveRealUserId(tokenData, refreshedUserId);
|
|
903
|
+
await this.assertStoredAccessToken(apiReq, token, tokenData);
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
throw error;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
748
909
|
}
|
|
749
910
|
apiReq.token = token;
|
|
750
911
|
return tokenData;
|
|
@@ -786,6 +947,9 @@ export class ApiServer {
|
|
|
786
947
|
}
|
|
787
948
|
async assertStoredAccessToken(apiReq, token, tokenData) {
|
|
788
949
|
const userId = String(this.extractTokenUserId(tokenData));
|
|
950
|
+
if (apiReq.authToken && apiReq.authToken.accessToken === token && String(apiReq.authToken.userId) === userId) {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
789
953
|
const stored = await this.storageAdapter.getToken({
|
|
790
954
|
accessToken: token,
|
|
791
955
|
userId
|
|
@@ -825,32 +989,98 @@ export class ApiServer {
|
|
|
825
989
|
}
|
|
826
990
|
return rawReal;
|
|
827
991
|
}
|
|
828
|
-
|
|
992
|
+
useExpress(pathOrHandler, ...handlers) {
|
|
993
|
+
if (typeof pathOrHandler === 'string') {
|
|
994
|
+
this.app.use(pathOrHandler, ...handlers);
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
this.app.use(pathOrHandler, ...handlers);
|
|
998
|
+
}
|
|
999
|
+
this.ensureApiNotFoundOrdering();
|
|
1000
|
+
return this;
|
|
1001
|
+
}
|
|
1002
|
+
createApiRequest(req, res) {
|
|
1003
|
+
const apiReq = {
|
|
1004
|
+
server: this,
|
|
1005
|
+
req,
|
|
1006
|
+
res,
|
|
1007
|
+
token: '',
|
|
1008
|
+
tokenData: null,
|
|
1009
|
+
realUid: null,
|
|
1010
|
+
getClientInfo: () => ensureClientInfo(apiReq),
|
|
1011
|
+
getClientIp: () => ensureClientInfo(apiReq).ip,
|
|
1012
|
+
getClientIpChain: () => ensureClientInfo(apiReq).ipchain,
|
|
1013
|
+
getRealUid: () => apiReq.realUid ?? null,
|
|
1014
|
+
isImpersonating: () => {
|
|
1015
|
+
const realUid = apiReq.realUid;
|
|
1016
|
+
const tokenUid = apiReq.tokenData?.uid;
|
|
1017
|
+
if (realUid === null || realUid === undefined) {
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|
|
1020
|
+
if (tokenUid === null || tokenUid === undefined) {
|
|
1021
|
+
return false;
|
|
1022
|
+
}
|
|
1023
|
+
return realUid !== tokenUid;
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
return apiReq;
|
|
1027
|
+
}
|
|
1028
|
+
expressAuth(auth) {
|
|
829
1029
|
return async (req, res, next) => {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
getClientIpChain: () => ensureClientInfo(apiReq).ipchain,
|
|
841
|
-
getRealUid: () => apiReq.realUid ?? null,
|
|
842
|
-
isImpersonating: () => {
|
|
843
|
-
const realUid = apiReq.realUid;
|
|
844
|
-
const tokenUid = apiReq.tokenData?.uid;
|
|
845
|
-
if (realUid === null || realUid === undefined) {
|
|
846
|
-
return false;
|
|
847
|
-
}
|
|
848
|
-
if (tokenUid === null || tokenUid === undefined) {
|
|
849
|
-
return false;
|
|
850
|
-
}
|
|
851
|
-
return realUid !== tokenUid;
|
|
1030
|
+
const apiReq = this.createApiRequest(req, res);
|
|
1031
|
+
req.apiReq = apiReq;
|
|
1032
|
+
res.locals.apiReq = apiReq;
|
|
1033
|
+
this.currReq = apiReq;
|
|
1034
|
+
try {
|
|
1035
|
+
if (this.config.hydrateGetBody) {
|
|
1036
|
+
hydrateGetBody(req);
|
|
1037
|
+
}
|
|
1038
|
+
if (this.config.debug) {
|
|
1039
|
+
this.dumpRequest(apiReq);
|
|
852
1040
|
}
|
|
1041
|
+
apiReq.tokenData = await this.authenticate(apiReq, auth.type);
|
|
1042
|
+
await this.authorize(apiReq, auth.req);
|
|
1043
|
+
next();
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
next(error);
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
expressErrorHandler() {
|
|
1051
|
+
return (error, _req, res, next) => {
|
|
1052
|
+
void _req;
|
|
1053
|
+
if (res.headersSent) {
|
|
1054
|
+
next(error);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
if (error instanceof ApiError || isApiErrorLike(error)) {
|
|
1058
|
+
const apiError = error;
|
|
1059
|
+
const normalizedErrors = apiError.errors && typeof apiError.errors === 'object' && !Array.isArray(apiError.errors)
|
|
1060
|
+
? apiError.errors
|
|
1061
|
+
: {};
|
|
1062
|
+
const errorPayload = {
|
|
1063
|
+
code: apiError.code,
|
|
1064
|
+
message: apiError.message,
|
|
1065
|
+
data: apiError.data ?? null,
|
|
1066
|
+
errors: normalizedErrors
|
|
1067
|
+
};
|
|
1068
|
+
res.status(apiError.code).json(errorPayload);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const errorPayload = {
|
|
1072
|
+
code: 500,
|
|
1073
|
+
message: this.guessExceptionText(error),
|
|
1074
|
+
data: null,
|
|
1075
|
+
errors: {}
|
|
853
1076
|
};
|
|
1077
|
+
res.status(500).json(errorPayload);
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
handle_request(handler, auth) {
|
|
1081
|
+
return async (req, res, next) => {
|
|
1082
|
+
void next;
|
|
1083
|
+
const apiReq = this.createApiRequest(req, res);
|
|
854
1084
|
this.currReq = apiReq;
|
|
855
1085
|
try {
|
|
856
1086
|
if (this.config.hydrateGetBody) {
|
|
@@ -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 {};
|