@technomoron/api-server-base 1.1.13 → 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 +448 -111
- package/dist/cjs/api-server-base.d.ts +91 -34
- package/dist/cjs/auth-api/auth-module.d.ts +105 -0
- package/dist/cjs/auth-api/auth-module.js +1180 -0
- package/dist/cjs/auth-api/compat-auth-storage.d.ts +57 -0
- package/dist/cjs/auth-api/compat-auth-storage.js +128 -0
- package/dist/cjs/auth-api/mem-auth-store.d.ts +68 -0
- package/dist/cjs/auth-api/mem-auth-store.js +141 -0
- package/dist/cjs/{auth-module.d.ts → auth-api/module.d.ts} +7 -7
- package/dist/cjs/{auth-module.cjs → auth-api/module.js} +1 -1
- package/dist/cjs/auth-api/sql-auth-store.d.ts +77 -0
- package/dist/cjs/auth-api/sql-auth-store.js +172 -0
- package/dist/cjs/auth-api/storage.d.ts +38 -0
- package/dist/cjs/{auth-storage.cjs → auth-api/storage.js} +17 -7
- package/dist/cjs/auth-api/types.d.ts +34 -0
- package/dist/cjs/auth-api/types.js +2 -0
- package/dist/cjs/index.cjs +41 -7
- package/dist/cjs/index.d.ts +29 -5
- package/dist/cjs/oauth/base.d.ts +10 -0
- package/dist/cjs/oauth/base.js +6 -0
- package/dist/cjs/oauth/memory.d.ts +16 -0
- package/dist/cjs/oauth/memory.js +99 -0
- package/dist/cjs/oauth/models.d.ts +45 -0
- package/dist/cjs/oauth/models.js +58 -0
- package/dist/cjs/oauth/sequelize.d.ts +68 -0
- package/dist/cjs/oauth/sequelize.js +210 -0
- package/dist/cjs/oauth/types.d.ts +50 -0
- package/dist/cjs/oauth/types.js +3 -0
- package/dist/cjs/passkey/base.d.ts +16 -0
- package/dist/cjs/passkey/base.js +6 -0
- package/dist/cjs/passkey/memory.d.ts +27 -0
- package/dist/cjs/passkey/memory.js +86 -0
- package/dist/cjs/passkey/models.d.ts +25 -0
- package/dist/cjs/passkey/models.js +115 -0
- package/dist/cjs/passkey/sequelize.d.ts +55 -0
- package/dist/cjs/passkey/sequelize.js +220 -0
- package/dist/cjs/passkey/service.d.ts +20 -0
- package/dist/cjs/passkey/service.js +356 -0
- package/dist/cjs/passkey/types.d.ts +78 -0
- package/dist/cjs/passkey/types.js +2 -0
- package/dist/cjs/token/base.d.ts +38 -0
- package/dist/cjs/token/base.js +114 -0
- package/dist/cjs/token/memory.d.ts +19 -0
- package/dist/cjs/token/memory.js +149 -0
- package/dist/cjs/token/sequelize.d.ts +58 -0
- package/dist/cjs/token/sequelize.js +404 -0
- package/dist/cjs/token/types.d.ts +27 -0
- package/dist/cjs/token/types.js +2 -0
- package/dist/cjs/user/base.d.ts +26 -0
- package/dist/cjs/user/base.js +45 -0
- package/dist/cjs/user/memory.d.ts +35 -0
- package/dist/cjs/user/memory.js +173 -0
- package/dist/cjs/user/sequelize.d.ts +41 -0
- package/dist/cjs/user/sequelize.js +182 -0
- package/dist/cjs/user/types.d.ts +11 -0
- package/dist/cjs/user/types.js +2 -0
- package/dist/esm/api-server-base.d.ts +91 -34
- package/dist/esm/api-server-base.js +447 -110
- package/dist/esm/auth-api/auth-module.d.ts +105 -0
- package/dist/esm/auth-api/auth-module.js +1178 -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 +137 -0
- package/dist/esm/{auth-module.d.ts → auth-api/module.d.ts} +7 -7
- package/dist/esm/{auth-module.js → auth-api/module.js} +1 -1
- package/dist/esm/auth-api/sql-auth-store.d.ts +77 -0
- package/dist/esm/auth-api/sql-auth-store.js +168 -0
- package/dist/esm/auth-api/storage.d.ts +38 -0
- package/dist/esm/{auth-storage.js → auth-api/storage.js} +15 -5
- package/dist/esm/auth-api/types.d.ts +34 -0
- package/dist/esm/auth-api/types.js +1 -0
- package/dist/esm/index.d.ts +29 -5
- package/dist/esm/index.js +19 -2
- package/dist/esm/oauth/base.d.ts +10 -0
- package/dist/esm/oauth/base.js +2 -0
- package/dist/esm/oauth/memory.d.ts +16 -0
- package/dist/esm/oauth/memory.js +92 -0
- package/dist/esm/oauth/models.d.ts +45 -0
- package/dist/esm/oauth/models.js +51 -0
- package/dist/esm/oauth/sequelize.d.ts +68 -0
- package/dist/esm/oauth/sequelize.js +199 -0
- package/dist/esm/oauth/types.d.ts +50 -0
- package/dist/esm/oauth/types.js +2 -0
- package/dist/esm/passkey/base.d.ts +16 -0
- package/dist/esm/passkey/base.js +2 -0
- package/dist/esm/passkey/memory.d.ts +27 -0
- package/dist/esm/passkey/memory.js +82 -0
- package/dist/esm/passkey/models.d.ts +25 -0
- package/dist/esm/passkey/models.js +108 -0
- package/dist/esm/passkey/sequelize.d.ts +55 -0
- package/dist/esm/passkey/sequelize.js +216 -0
- package/dist/esm/passkey/service.d.ts +20 -0
- package/dist/esm/passkey/service.js +319 -0
- package/dist/esm/passkey/types.d.ts +78 -0
- package/dist/esm/passkey/types.js +1 -0
- package/dist/esm/token/base.d.ts +38 -0
- package/dist/esm/token/base.js +107 -0
- package/dist/esm/token/memory.d.ts +19 -0
- package/dist/esm/token/memory.js +145 -0
- package/dist/esm/token/sequelize.d.ts +58 -0
- package/dist/esm/token/sequelize.js +400 -0
- package/dist/esm/token/types.d.ts +27 -0
- package/dist/esm/token/types.js +1 -0
- package/dist/esm/user/base.d.ts +26 -0
- package/dist/esm/user/base.js +38 -0
- package/dist/esm/user/memory.d.ts +35 -0
- package/dist/esm/user/memory.js +169 -0
- package/dist/esm/user/sequelize.d.ts +41 -0
- package/dist/esm/user/sequelize.js +176 -0
- package/dist/esm/user/types.d.ts +11 -0
- package/dist/esm/user/types.js +1 -0
- package/package.json +13 -3
- package/dist/cjs/auth-storage.d.ts +0 -133
- package/dist/esm/auth-storage.d.ts +0 -133
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AuthAdapter, AuthIdentifier } from './types.js';
|
|
2
|
+
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
3
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult, StoredPasskeyCredential } from '../passkey/types.js';
|
|
4
|
+
import type { Token } from '../token/types.js';
|
|
5
|
+
export declare class BaseAuthAdapter<UserRow = unknown, SafeUser = unknown> implements AuthAdapter<UserRow, SafeUser> {
|
|
6
|
+
getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
|
|
7
|
+
getUserPasswordHash(user: UserRow): string;
|
|
8
|
+
getUserId(user: UserRow): AuthIdentifier;
|
|
9
|
+
filterUser(user: UserRow): SafeUser;
|
|
10
|
+
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
11
|
+
storeToken(data: Token): Promise<void>;
|
|
12
|
+
getToken(query: Partial<Omit<Token, 'userId' | 'ruid'>> & {
|
|
13
|
+
userId?: string | number;
|
|
14
|
+
ruid?: string | number;
|
|
15
|
+
}, opts?: {
|
|
16
|
+
includeExpired?: boolean;
|
|
17
|
+
}): Promise<Token | null>;
|
|
18
|
+
deleteToken(query: Partial<Omit<Token, 'userId' | 'ruid'>> & {
|
|
19
|
+
userId?: string | number;
|
|
20
|
+
ruid?: string | number;
|
|
21
|
+
}): Promise<number>;
|
|
22
|
+
updateToken(updates: Partial<Token> & {
|
|
23
|
+
refreshToken: string;
|
|
24
|
+
}): Promise<boolean>;
|
|
25
|
+
createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
26
|
+
verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
27
|
+
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
28
|
+
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
29
|
+
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
30
|
+
verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
31
|
+
createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
32
|
+
consumeAuthCode(code: string, clientId: string): Promise<AuthCodeData | null>;
|
|
33
|
+
canImpersonate(params: {
|
|
34
|
+
realUserId: AuthIdentifier;
|
|
35
|
+
effectiveUserId: AuthIdentifier;
|
|
36
|
+
}): Promise<boolean>;
|
|
37
|
+
}
|
|
38
|
+
export declare const nullAuthAdapter: AuthAdapter<unknown, unknown>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Handy base you can extend when wiring a real storage adapter. Every method
|
|
1
|
+
// Handy base you can extend when wiring a real auth adapter. Every method
|
|
3
2
|
// throws by default so unimplemented hooks fail loudly.
|
|
4
|
-
export class
|
|
3
|
+
export class BaseAuthAdapter {
|
|
5
4
|
// Override to load a user record by identifier
|
|
6
5
|
async getUser(identifier) {
|
|
7
6
|
void identifier;
|
|
@@ -33,8 +32,9 @@ export class BaseAuthStorage {
|
|
|
33
32
|
throw new Error('Auth storage not configured');
|
|
34
33
|
}
|
|
35
34
|
// Override to look up a stored token by query
|
|
36
|
-
async getToken(query) {
|
|
35
|
+
async getToken(query, opts) {
|
|
37
36
|
void query;
|
|
37
|
+
void opts;
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
40
|
// Override to remove stored tokens that match the query
|
|
@@ -57,6 +57,16 @@ export class BaseAuthStorage {
|
|
|
57
57
|
void params;
|
|
58
58
|
throw new Error('Auth storage not configured');
|
|
59
59
|
}
|
|
60
|
+
// Override to list passkey credentials for a user
|
|
61
|
+
async listUserCredentials(userId) {
|
|
62
|
+
void userId;
|
|
63
|
+
throw new Error('Auth storage not configured');
|
|
64
|
+
}
|
|
65
|
+
// Override to delete a passkey credential
|
|
66
|
+
async deletePasskeyCredential(credentialId) {
|
|
67
|
+
void credentialId;
|
|
68
|
+
throw new Error('Auth storage not configured');
|
|
69
|
+
}
|
|
60
70
|
// Override to fetch an OAuth client by identifier
|
|
61
71
|
async getClient(clientId) {
|
|
62
72
|
void clientId;
|
|
@@ -85,4 +95,4 @@ export class BaseAuthStorage {
|
|
|
85
95
|
return false;
|
|
86
96
|
}
|
|
87
97
|
}
|
|
88
|
-
export const
|
|
98
|
+
export const nullAuthAdapter = new BaseAuthAdapter();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
|
|
2
|
+
import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult, StoredPasskeyCredential } from '../passkey/types.js';
|
|
3
|
+
import type { Token } from '../token/types.js';
|
|
4
|
+
export type AuthIdentifier = string | number;
|
|
5
|
+
/** @internal */
|
|
6
|
+
export interface AuthAdapter<UserRow, SafeUser> {
|
|
7
|
+
getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
|
|
8
|
+
getUserPasswordHash(user: UserRow): string;
|
|
9
|
+
getUserId(user: UserRow): AuthIdentifier;
|
|
10
|
+
filterUser(user: UserRow): SafeUser;
|
|
11
|
+
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
12
|
+
storeToken(data: Token): Promise<void>;
|
|
13
|
+
getToken(query: Partial<Token>, opts?: {
|
|
14
|
+
includeExpired?: boolean;
|
|
15
|
+
}): Promise<Token | null>;
|
|
16
|
+
deleteToken(query: Partial<Token>): Promise<number>;
|
|
17
|
+
updateToken?(updates: Partial<Token> & {
|
|
18
|
+
refreshToken: string;
|
|
19
|
+
}): Promise<boolean>;
|
|
20
|
+
createPasskeyChallenge?(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
21
|
+
verifyPasskeyResponse?(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
22
|
+
listUserCredentials?(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
23
|
+
deletePasskeyCredential?(credentialId: Buffer | string): Promise<boolean>;
|
|
24
|
+
getClient?(clientId: string): Promise<OAuthClient | null>;
|
|
25
|
+
verifyClientSecret?(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
|
|
26
|
+
createAuthCode?(request: AuthCodeRequest): Promise<AuthCodeData>;
|
|
27
|
+
consumeAuthCode?(code: string, clientId: string): Promise<AuthCodeData | null>;
|
|
28
|
+
canImpersonate?(params: {
|
|
29
|
+
realUserId: AuthIdentifier;
|
|
30
|
+
effectiveUserId: AuthIdentifier;
|
|
31
|
+
}): Promise<boolean>;
|
|
32
|
+
}
|
|
33
|
+
/** @internal */
|
|
34
|
+
export type AuthStorage<UserRow, SafeUser> = AuthAdapter<UserRow, SafeUser>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
export { default as ApiServer } from './api-server-base.js';
|
|
2
2
|
export { ApiError } from './api-server-base.js';
|
|
3
3
|
export { ApiModule } from './api-module.js';
|
|
4
|
-
export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData,
|
|
5
|
-
export type { AuthIdentifier
|
|
6
|
-
export type {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
4
|
+
export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData, ExtendedReq, ExpressApiRequest, ExpressApiLocals } from './api-server-base.js';
|
|
5
|
+
export type { AuthIdentifier } from './auth-api/types.js';
|
|
6
|
+
export type { Token, TokenPair, TokenStatus } from './token/types.js';
|
|
7
|
+
export type { JwtSignResult, JwtVerifyResult, JwtDecodeResult } from './token/base.js';
|
|
8
|
+
export type { OAuthClient, AuthCodeData, AuthCodeRequest } from './oauth/types.js';
|
|
9
|
+
export type { AuthProviderModule } from './auth-api/module.js';
|
|
10
|
+
export { nullAuthAdapter, BaseAuthAdapter } from './auth-api/storage.js';
|
|
11
|
+
export { nullAuthModule, BaseAuthModule } from './auth-api/module.js';
|
|
12
|
+
export { CompositeAuthAdapter } from './auth-api/compat-auth-storage.js';
|
|
13
|
+
export { MemAuthStore } from './auth-api/mem-auth-store.js';
|
|
14
|
+
export { SqlAuthStore } from './auth-api/sql-auth-store.js';
|
|
15
|
+
export { default as AuthModule } from './auth-api/auth-module.js';
|
|
16
|
+
export type { OAuthStartParams, OAuthStartResult, OAuthCallbackParams, OAuthCallbackResult } from './oauth/types.js';
|
|
17
|
+
export type { BcryptHasherOptions, CreateUserInput, UpdateUserInput, PublicUserMapper } from './user/types.js';
|
|
18
|
+
export { UserStore } from './user/base.js';
|
|
19
|
+
export { MemoryUserStore } from './user/memory.js';
|
|
20
|
+
export { SequelizeUserStore } from './user/sequelize.js';
|
|
21
|
+
export type { MemoryUserAttributes, MemoryUserStoreOptions } from './user/memory.js';
|
|
22
|
+
export { TokenStore } from './token/base.js';
|
|
23
|
+
export { MemoryTokenStore } from './token/memory.js';
|
|
24
|
+
export { SequelizeTokenStore } from './token/sequelize.js';
|
|
25
|
+
export { PasskeyService } from './passkey/service.js';
|
|
26
|
+
export { PasskeyStore } from './passkey/base.js';
|
|
27
|
+
export { MemoryPasskeyStore } from './passkey/memory.js';
|
|
28
|
+
export { SequelizePasskeyStore } from './passkey/sequelize.js';
|
|
29
|
+
export type { PasskeyServiceConfig, PasskeyChallengeRecord, PasskeyUserDescriptor, StoredPasskeyCredential, PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from './passkey/types.js';
|
|
30
|
+
export { OAuthStore } from './oauth/base.js';
|
|
31
|
+
export { MemoryOAuthStore } from './oauth/memory.js';
|
|
32
|
+
export { SequelizeOAuthStore } from './oauth/sequelize.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
export { default as ApiServer } from './api-server-base.js';
|
|
2
2
|
export { ApiError } from './api-server-base.js';
|
|
3
3
|
export { ApiModule } from './api-module.js';
|
|
4
|
-
export {
|
|
5
|
-
export { nullAuthModule, BaseAuthModule } from './auth-module.js';
|
|
4
|
+
export { nullAuthAdapter, BaseAuthAdapter } from './auth-api/storage.js';
|
|
5
|
+
export { nullAuthModule, BaseAuthModule } from './auth-api/module.js';
|
|
6
|
+
export { CompositeAuthAdapter } from './auth-api/compat-auth-storage.js';
|
|
7
|
+
export { MemAuthStore } from './auth-api/mem-auth-store.js';
|
|
8
|
+
export { SqlAuthStore } from './auth-api/sql-auth-store.js';
|
|
9
|
+
export { default as AuthModule } from './auth-api/auth-module.js';
|
|
10
|
+
export { UserStore } from './user/base.js';
|
|
11
|
+
export { MemoryUserStore } from './user/memory.js';
|
|
12
|
+
export { SequelizeUserStore } from './user/sequelize.js';
|
|
13
|
+
export { TokenStore } from './token/base.js';
|
|
14
|
+
export { MemoryTokenStore } from './token/memory.js';
|
|
15
|
+
export { SequelizeTokenStore } from './token/sequelize.js';
|
|
16
|
+
export { PasskeyService } from './passkey/service.js';
|
|
17
|
+
export { PasskeyStore } from './passkey/base.js';
|
|
18
|
+
export { MemoryPasskeyStore } from './passkey/memory.js';
|
|
19
|
+
export { SequelizePasskeyStore } from './passkey/sequelize.js';
|
|
20
|
+
export { OAuthStore } from './oauth/base.js';
|
|
21
|
+
export { MemoryOAuthStore } from './oauth/memory.js';
|
|
22
|
+
export { SequelizeOAuthStore } from './oauth/sequelize.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AuthCode, OAuthClient } from './types.js';
|
|
2
|
+
export declare abstract class OAuthStore {
|
|
3
|
+
abstract getClient(clientId: string): Promise<OAuthClient | null>;
|
|
4
|
+
abstract createClient(input: OAuthClient): Promise<OAuthClient>;
|
|
5
|
+
abstract verifyClientSecret(clientId: string, secret: string | null): Promise<boolean>;
|
|
6
|
+
abstract createAuthCode(code: AuthCode): Promise<void>;
|
|
7
|
+
abstract consumeAuthCode(code: string): Promise<AuthCode | null>;
|
|
8
|
+
abstract close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export type { OAuthClient, AuthCode } from './types.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { OAuthStore, type AuthCode, type OAuthClient } from './base.js';
|
|
2
|
+
export interface MemoryOAuthStoreOptions {
|
|
3
|
+
bcryptRounds?: number;
|
|
4
|
+
}
|
|
5
|
+
export declare class MemoryOAuthStore extends OAuthStore {
|
|
6
|
+
private readonly clients;
|
|
7
|
+
private readonly codes;
|
|
8
|
+
private readonly bcryptRounds;
|
|
9
|
+
constructor(options?: MemoryOAuthStoreOptions);
|
|
10
|
+
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
11
|
+
createClient(input: OAuthClient): Promise<OAuthClient>;
|
|
12
|
+
verifyClientSecret(clientId: string, secret: string | null): Promise<boolean>;
|
|
13
|
+
createAuthCode(code: AuthCode): Promise<void>;
|
|
14
|
+
consumeAuthCode(code: string): Promise<AuthCode | null>;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import bcrypt from 'bcryptjs';
|
|
2
|
+
import { OAuthStore } from './base.js';
|
|
3
|
+
function cloneClient(client) {
|
|
4
|
+
if (!client) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
clientId: client.clientId,
|
|
9
|
+
clientSecret: client.clientSecret,
|
|
10
|
+
name: client.name,
|
|
11
|
+
redirectUris: [...client.redirectUris],
|
|
12
|
+
scope: client.scope ? [...client.scope] : undefined,
|
|
13
|
+
metadata: client.metadata ? { ...client.metadata } : undefined,
|
|
14
|
+
firstParty: client.firstParty
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function cloneCode(code) {
|
|
18
|
+
return {
|
|
19
|
+
...code,
|
|
20
|
+
scope: code.scope ? [...code.scope] : undefined,
|
|
21
|
+
expiresAt: new Date(code.expiresAt),
|
|
22
|
+
metadata: code.metadata ? { ...code.metadata } : undefined
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function normalizeUserId(identifier) {
|
|
26
|
+
if (typeof identifier === 'number' && Number.isFinite(identifier)) {
|
|
27
|
+
return identifier;
|
|
28
|
+
}
|
|
29
|
+
if (typeof identifier === 'string' && /^\d+$/.test(identifier)) {
|
|
30
|
+
return Number(identifier);
|
|
31
|
+
}
|
|
32
|
+
throw new Error(`Unable to normalise user identifier: ${identifier}`);
|
|
33
|
+
}
|
|
34
|
+
export class MemoryOAuthStore extends OAuthStore {
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
super();
|
|
37
|
+
this.clients = new Map();
|
|
38
|
+
this.codes = new Map();
|
|
39
|
+
this.bcryptRounds = options.bcryptRounds ?? 12;
|
|
40
|
+
}
|
|
41
|
+
async getClient(clientId) {
|
|
42
|
+
return cloneClient(this.clients.get(clientId));
|
|
43
|
+
}
|
|
44
|
+
async createClient(input) {
|
|
45
|
+
const clientSecret = input.clientSecret ? await bcrypt.hash(input.clientSecret, this.bcryptRounds) : '';
|
|
46
|
+
const stored = {
|
|
47
|
+
clientId: input.clientId,
|
|
48
|
+
clientSecret,
|
|
49
|
+
name: input.name,
|
|
50
|
+
redirectUris: [...input.redirectUris],
|
|
51
|
+
scope: input.scope ? [...input.scope] : undefined,
|
|
52
|
+
metadata: input.metadata ? { ...input.metadata } : undefined,
|
|
53
|
+
firstParty: input.firstParty
|
|
54
|
+
};
|
|
55
|
+
this.clients.set(stored.clientId, stored);
|
|
56
|
+
return cloneClient(stored);
|
|
57
|
+
}
|
|
58
|
+
async verifyClientSecret(clientId, secret) {
|
|
59
|
+
const client = this.clients.get(clientId);
|
|
60
|
+
if (!client) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (!client.clientSecret) {
|
|
64
|
+
return !secret || secret.length === 0;
|
|
65
|
+
}
|
|
66
|
+
if (!secret) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return bcrypt.compare(secret, client.clientSecret);
|
|
70
|
+
}
|
|
71
|
+
async createAuthCode(code) {
|
|
72
|
+
const record = {
|
|
73
|
+
...code,
|
|
74
|
+
userId: normalizeUserId(code.userId),
|
|
75
|
+
scope: code.scope ? [...code.scope] : undefined,
|
|
76
|
+
expiresAt: code.expiresAt,
|
|
77
|
+
metadata: code.metadata ? { ...code.metadata } : undefined
|
|
78
|
+
};
|
|
79
|
+
this.codes.set(record.code, record);
|
|
80
|
+
}
|
|
81
|
+
async consumeAuthCode(code) {
|
|
82
|
+
const record = this.codes.get(code);
|
|
83
|
+
if (!record) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
this.codes.delete(code);
|
|
87
|
+
return cloneCode(record);
|
|
88
|
+
}
|
|
89
|
+
async close() {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Model, type Optional, type Sequelize } from 'sequelize';
|
|
2
|
+
export interface OAuthClientAttributes {
|
|
3
|
+
client_id: string;
|
|
4
|
+
client_secret: string;
|
|
5
|
+
name: string | null;
|
|
6
|
+
redirect_uris: string;
|
|
7
|
+
scope: string;
|
|
8
|
+
metadata: string | null;
|
|
9
|
+
first_party: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type OAuthClientCreationAttributes = Optional<OAuthClientAttributes, 'client_secret' | 'name' | 'scope' | 'metadata' | 'first_party'>;
|
|
12
|
+
export declare class OAuthClientModel extends Model<OAuthClientAttributes, OAuthClientCreationAttributes> implements OAuthClientAttributes {
|
|
13
|
+
client_id: string;
|
|
14
|
+
client_secret: string;
|
|
15
|
+
name: string | null;
|
|
16
|
+
redirect_uris: string;
|
|
17
|
+
scope: string;
|
|
18
|
+
metadata: string | null;
|
|
19
|
+
first_party: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function initOAuthClientModel(sequelize: Sequelize): typeof OAuthClientModel;
|
|
22
|
+
export interface OAuthCodeAttributes {
|
|
23
|
+
code: string;
|
|
24
|
+
client_id: string;
|
|
25
|
+
user_id: number;
|
|
26
|
+
redirect_uri: string;
|
|
27
|
+
scope: string;
|
|
28
|
+
code_challenge: string | null;
|
|
29
|
+
code_challenge_method: 'plain' | 'S256' | null;
|
|
30
|
+
expires: Date;
|
|
31
|
+
metadata: string | null;
|
|
32
|
+
}
|
|
33
|
+
export type OAuthCodeCreationAttributes = Optional<OAuthCodeAttributes, 'code_challenge' | 'code_challenge_method' | 'metadata'>;
|
|
34
|
+
export declare class OAuthCodeModel extends Model<OAuthCodeAttributes, OAuthCodeCreationAttributes> implements OAuthCodeAttributes {
|
|
35
|
+
code: string;
|
|
36
|
+
client_id: string;
|
|
37
|
+
user_id: number;
|
|
38
|
+
redirect_uri: string;
|
|
39
|
+
scope: string;
|
|
40
|
+
code_challenge: string | null;
|
|
41
|
+
code_challenge_method: 'plain' | 'S256' | null;
|
|
42
|
+
expires: Date;
|
|
43
|
+
metadata: string | null;
|
|
44
|
+
}
|
|
45
|
+
export declare function initOAuthCodeModel(sequelize: Sequelize): typeof OAuthCodeModel;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
|
|
3
|
+
function integerIdType(sequelize) {
|
|
4
|
+
return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? DataTypes.INTEGER.UNSIGNED : DataTypes.INTEGER;
|
|
5
|
+
}
|
|
6
|
+
function tableOptions(sequelize, tableName, extra) {
|
|
7
|
+
const opts = { sequelize, tableName };
|
|
8
|
+
if (extra) {
|
|
9
|
+
Object.assign(opts, extra);
|
|
10
|
+
}
|
|
11
|
+
if (DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())) {
|
|
12
|
+
opts.charset = 'utf8mb4';
|
|
13
|
+
opts.collate = 'utf8mb4_unicode_ci';
|
|
14
|
+
}
|
|
15
|
+
return opts;
|
|
16
|
+
}
|
|
17
|
+
export class OAuthClientModel extends Model {
|
|
18
|
+
}
|
|
19
|
+
export function initOAuthClientModel(sequelize) {
|
|
20
|
+
OAuthClientModel.init({
|
|
21
|
+
client_id: { type: DataTypes.STRING(128), allowNull: false, primaryKey: true },
|
|
22
|
+
client_secret: { type: DataTypes.STRING(255), allowNull: false, defaultValue: '' },
|
|
23
|
+
name: { type: DataTypes.STRING(128), allowNull: true, defaultValue: null },
|
|
24
|
+
redirect_uris: { type: DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
|
|
25
|
+
scope: { type: DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
|
|
26
|
+
metadata: { type: DataTypes.TEXT, allowNull: true, defaultValue: null },
|
|
27
|
+
first_party: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
|
|
28
|
+
}, {
|
|
29
|
+
...tableOptions(sequelize, 'oauth_clients', { timestamps: false })
|
|
30
|
+
});
|
|
31
|
+
return OAuthClientModel;
|
|
32
|
+
}
|
|
33
|
+
export class OAuthCodeModel extends Model {
|
|
34
|
+
}
|
|
35
|
+
export function initOAuthCodeModel(sequelize) {
|
|
36
|
+
const idType = integerIdType(sequelize);
|
|
37
|
+
OAuthCodeModel.init({
|
|
38
|
+
code: { type: DataTypes.STRING(128), allowNull: false, primaryKey: true },
|
|
39
|
+
client_id: { type: DataTypes.STRING(128), allowNull: false },
|
|
40
|
+
user_id: { type: idType, allowNull: false },
|
|
41
|
+
redirect_uri: { type: DataTypes.TEXT, allowNull: false },
|
|
42
|
+
scope: { type: DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
|
|
43
|
+
code_challenge: { type: DataTypes.STRING(255), allowNull: true, defaultValue: null },
|
|
44
|
+
code_challenge_method: { type: DataTypes.STRING(10), allowNull: true, defaultValue: null },
|
|
45
|
+
expires: { type: DataTypes.DATE, allowNull: false },
|
|
46
|
+
metadata: { type: DataTypes.TEXT, allowNull: true, defaultValue: null }
|
|
47
|
+
}, {
|
|
48
|
+
...tableOptions(sequelize, 'oauth_codes', { timestamps: false })
|
|
49
|
+
});
|
|
50
|
+
return OAuthCodeModel;
|
|
51
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Model, type Optional, type Sequelize } from 'sequelize';
|
|
2
|
+
import { OAuthStore, type AuthCode, type OAuthClient } from './base.js';
|
|
3
|
+
export interface OAuthClientAttributes {
|
|
4
|
+
client_id: string;
|
|
5
|
+
client_secret: string;
|
|
6
|
+
name: string | null;
|
|
7
|
+
redirect_uris: string;
|
|
8
|
+
scope: string;
|
|
9
|
+
metadata: string | null;
|
|
10
|
+
first_party: boolean;
|
|
11
|
+
}
|
|
12
|
+
export type OAuthClientCreationAttributes = Optional<OAuthClientAttributes, 'client_secret' | 'name' | 'scope' | 'metadata' | 'first_party'>;
|
|
13
|
+
export declare class OAuthClientModel extends Model<OAuthClientAttributes, OAuthClientCreationAttributes> implements OAuthClientAttributes {
|
|
14
|
+
client_id: string;
|
|
15
|
+
client_secret: string;
|
|
16
|
+
name: string | null;
|
|
17
|
+
redirect_uris: string;
|
|
18
|
+
scope: string;
|
|
19
|
+
metadata: string | null;
|
|
20
|
+
first_party: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function initOAuthClientModel(sequelize: Sequelize): typeof OAuthClientModel;
|
|
23
|
+
export interface OAuthCodeAttributes {
|
|
24
|
+
code: string;
|
|
25
|
+
client_id: string;
|
|
26
|
+
user_id: number;
|
|
27
|
+
redirect_uri: string;
|
|
28
|
+
scope: string;
|
|
29
|
+
code_challenge: string | null;
|
|
30
|
+
code_challenge_method: 'plain' | 'S256' | null;
|
|
31
|
+
expires: Date;
|
|
32
|
+
metadata: string | null;
|
|
33
|
+
}
|
|
34
|
+
export type OAuthCodeCreationAttributes = Optional<OAuthCodeAttributes, 'code_challenge' | 'code_challenge_method' | 'metadata'>;
|
|
35
|
+
export declare class OAuthCodeModel extends Model<OAuthCodeAttributes, OAuthCodeCreationAttributes> implements OAuthCodeAttributes {
|
|
36
|
+
code: string;
|
|
37
|
+
client_id: string;
|
|
38
|
+
user_id: number;
|
|
39
|
+
redirect_uri: string;
|
|
40
|
+
scope: string;
|
|
41
|
+
code_challenge: string | null;
|
|
42
|
+
code_challenge_method: 'plain' | 'S256' | null;
|
|
43
|
+
expires: Date;
|
|
44
|
+
metadata: string | null;
|
|
45
|
+
}
|
|
46
|
+
export declare function initOAuthCodeModel(sequelize: Sequelize): typeof OAuthCodeModel;
|
|
47
|
+
export interface SequelizeOAuthStoreOptions {
|
|
48
|
+
sequelize: Sequelize;
|
|
49
|
+
clientModel?: typeof OAuthClientModel;
|
|
50
|
+
codeModel?: typeof OAuthCodeModel;
|
|
51
|
+
clientModelFactory?: (sequelize: Sequelize) => typeof OAuthClientModel;
|
|
52
|
+
codeModelFactory?: (sequelize: Sequelize) => typeof OAuthCodeModel;
|
|
53
|
+
bcryptRounds?: number;
|
|
54
|
+
}
|
|
55
|
+
export declare class SequelizeOAuthStore extends OAuthStore {
|
|
56
|
+
private readonly clients;
|
|
57
|
+
private readonly codes;
|
|
58
|
+
private readonly bcryptRounds;
|
|
59
|
+
constructor(options: SequelizeOAuthStoreOptions);
|
|
60
|
+
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
61
|
+
createClient(input: OAuthClient): Promise<OAuthClient>;
|
|
62
|
+
verifyClientSecret(clientId: string, clientSecret: string | null): Promise<boolean>;
|
|
63
|
+
createAuthCode(code: AuthCode): Promise<void>;
|
|
64
|
+
consumeAuthCode(code: string): Promise<AuthCode | null>;
|
|
65
|
+
close(): Promise<void>;
|
|
66
|
+
private toOAuthClient;
|
|
67
|
+
private toAuthCode;
|
|
68
|
+
}
|