390 lines
7.9 KiB
TypeScript
390 lines
7.9 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import { OAuth2Logger } from '../utils/logger';
|
|
|
|
/**
|
|
* OAuth2 client object
|
|
*/
|
|
export interface OAuth2Client {
|
|
id: string | number;
|
|
secret: string;
|
|
scope: string[];
|
|
grants: string[];
|
|
}
|
|
|
|
/**
|
|
* OAuth2 access token object
|
|
*/
|
|
export interface OAuth2AccessToken {
|
|
token: string;
|
|
user_id: string | number;
|
|
client_id: string | number;
|
|
scope: string;
|
|
expires_at: Date;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 authorization code object
|
|
*/
|
|
export interface OAuth2Code {
|
|
code: string;
|
|
expires_at: Date;
|
|
user_id: string | number;
|
|
client_id: string | number;
|
|
scope: string;
|
|
nonce?: string;
|
|
code_challenge_method?: 'plain' | 'S256';
|
|
code_challenge?: string;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 refresh token object
|
|
*/
|
|
export interface OAuth2RefreshToken {
|
|
token: string;
|
|
user_id: string | number;
|
|
client_id: string | number;
|
|
scope: string;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 implicit user model
|
|
*/
|
|
export interface OAuth2User {
|
|
id: string | number;
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 token response
|
|
*/
|
|
export interface OAuth2TokenResponse {
|
|
id_token?: string;
|
|
access_token?: string;
|
|
refresh_token?: string;
|
|
expires_in?: number;
|
|
token_type?: string;
|
|
state?: string;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 access token adapter model
|
|
*/
|
|
export interface OAuth2AccessTokenAdapter {
|
|
/**
|
|
* Static time-to-live in seconds for access token
|
|
*/
|
|
ttl: number;
|
|
|
|
/**
|
|
* Get the token string from an access token object
|
|
*/
|
|
getToken: (token: OAuth2AccessToken) => string;
|
|
|
|
/**
|
|
* Create a new access token
|
|
*/
|
|
create: (
|
|
userId: string | number | null,
|
|
clientId: string | number,
|
|
scope: string | string[],
|
|
ttl: number
|
|
) => Promise<string>;
|
|
|
|
/**
|
|
* Fetch an access token by the token string from the database
|
|
*/
|
|
fetchByToken: (
|
|
token: OAuth2AccessToken | string
|
|
) => Promise<OAuth2AccessToken>;
|
|
|
|
/**
|
|
* Check the time-to-live value
|
|
*/
|
|
checkTTL: (token: OAuth2AccessToken) => boolean;
|
|
|
|
/**
|
|
* Get the time-to-live value from a token object
|
|
*/
|
|
getTTL: (token: OAuth2AccessToken) => number;
|
|
|
|
/**
|
|
* Fetch an access token by user ID and client ID from the database
|
|
*/
|
|
fetchByUserIdClientId: (
|
|
userId: string | number,
|
|
clientId: string | number
|
|
) => Promise<OAuth2AccessToken>;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 client adapter model
|
|
*/
|
|
export interface OAuth2ClientAdapter {
|
|
/**
|
|
* Get client ID from client object
|
|
*/
|
|
getId: (client: OAuth2Client) => string | number;
|
|
|
|
/**
|
|
* Get client object from the database by client ID
|
|
*/
|
|
fetchById: (id: string | number) => Promise<OAuth2Client>;
|
|
|
|
/**
|
|
* Check the client secret
|
|
*/
|
|
checkSecret: (client: OAuth2Client, secret: string) => boolean;
|
|
|
|
/**
|
|
* Check grant type
|
|
*/
|
|
checkGrantType: (client: OAuth2Client, grant: string) => boolean;
|
|
|
|
/**
|
|
* Get the redirect uri of a client
|
|
*/
|
|
hasRedirectUri: (client: OAuth2Client) => Promise<boolean>;
|
|
|
|
/**
|
|
* Check the redirect uri against a client
|
|
*/
|
|
checkRedirectUri: (
|
|
client: OAuth2Client,
|
|
redirectUri: string
|
|
) => Promise<boolean>;
|
|
|
|
/**
|
|
* Transform the scope into a string array of scopes
|
|
*/
|
|
transformScope: (scope: string | string[]) => string[];
|
|
|
|
/**
|
|
* Check scopes against client
|
|
*/
|
|
checkScope: (client: OAuth2Client, scope: string[]) => boolean;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 response code adapter model
|
|
*/
|
|
export interface OAuth2CodeAdapter {
|
|
/**
|
|
* Static time-to-live in seconds for code
|
|
*/
|
|
ttl: number;
|
|
|
|
/**
|
|
* Create a new code
|
|
*/
|
|
create: (
|
|
userId: string | number,
|
|
clientId: string | number,
|
|
scope: string | string[],
|
|
ttl: number,
|
|
nonce?: string,
|
|
code_challenge?: string,
|
|
code_challenge_method?: 'plain' | 'S256',
|
|
) => Promise<string>;
|
|
|
|
/**
|
|
* Fetch a code by the code string from the database
|
|
*/
|
|
fetchByCode: (code: OAuth2Code | string) => Promise<OAuth2Code>;
|
|
|
|
/**
|
|
* Remove a code
|
|
*/
|
|
removeByCode: (code: string | OAuth2Code) => Promise<boolean>;
|
|
|
|
/**
|
|
* Get user ID from a code object
|
|
*/
|
|
getUserId: (code: OAuth2Code) => string | number;
|
|
|
|
/**
|
|
* Get client ID from a code object
|
|
*/
|
|
getClientId: (code: OAuth2Code) => string | number;
|
|
|
|
/**
|
|
* Get scope from a code object
|
|
*/
|
|
getScope: (code: OAuth2Code) => string;
|
|
|
|
/**
|
|
* Check the time-to-live value
|
|
*/
|
|
checkTTL: (code: OAuth2Code) => boolean;
|
|
|
|
/**
|
|
* Get PCKE info
|
|
*/
|
|
getCodeChallenge?: (code: OAuth2Code) => { method: 'plain' | 'S256'; challenge: string; }
|
|
}
|
|
|
|
/**
|
|
* OAuth2 refresh token adapter model
|
|
*/
|
|
export interface OAuth2RefreshTokenAdapter {
|
|
/**
|
|
* Invalidate all previous refresh tokens for user/client when new one is issued.
|
|
*/
|
|
invalidateOld: boolean;
|
|
|
|
/**
|
|
* Create a new refresh token
|
|
*/
|
|
create: (
|
|
userId: string | number,
|
|
clientId: string | number,
|
|
scope: string | string[]
|
|
) => Promise<string>;
|
|
|
|
/**
|
|
* Fetch a token from the database
|
|
*/
|
|
fetchByToken: (
|
|
token: OAuth2RefreshToken | string
|
|
) => Promise<OAuth2RefreshToken>;
|
|
|
|
/**
|
|
* Remove refresh token by user ID and client ID
|
|
*/
|
|
removeByUserIdClientId: (
|
|
userId: string | number,
|
|
clientId: string | number
|
|
) => Promise<boolean>;
|
|
|
|
/**
|
|
* Remove token by the token itself
|
|
*/
|
|
removeByRefreshToken: (token: string) => Promise<boolean>;
|
|
|
|
/**
|
|
* Get user ID from token
|
|
*/
|
|
getUserId: (token: OAuth2RefreshToken) => string | number;
|
|
|
|
/**
|
|
* Get client ID from token
|
|
*/
|
|
getClientId: (token: OAuth2RefreshToken) => string | number;
|
|
|
|
/**
|
|
* Get scope from token
|
|
*/
|
|
getScope: (token: OAuth2RefreshToken) => string;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 user adapter model
|
|
*/
|
|
export interface OAuth2UserAdapter {
|
|
/**
|
|
* Get user ID
|
|
*/
|
|
getId: (user: OAuth2User) => string | number;
|
|
|
|
/**
|
|
* Get user from the database by ID
|
|
*/
|
|
fetchById: (id: string | number) => Promise<OAuth2User>;
|
|
|
|
/**
|
|
* Fetch a user from the database by username
|
|
*/
|
|
fetchByUsername: (username: string) => Promise<OAuth2User>;
|
|
|
|
/**
|
|
* Check user's password for implicit grant
|
|
*/
|
|
checkPassword: (user: OAuth2User, password: string) => Promise<boolean>;
|
|
|
|
/**
|
|
* Fetch the user from the express request
|
|
*/
|
|
fetchFromRequest: (req: Request) => Promise<OAuth2User>;
|
|
|
|
/**
|
|
* Check if the user has already consented to this client and the scopes. Return false to force a decision.
|
|
*/
|
|
consented: (
|
|
userId: string | number,
|
|
clientId: string | number,
|
|
scope: string | string[]
|
|
) => Promise<boolean>;
|
|
|
|
/**
|
|
* Save a consent
|
|
*/
|
|
consent: (
|
|
userId: string | number,
|
|
clientId: string | number,
|
|
scope: string | string[]
|
|
) => Promise<boolean>;
|
|
}
|
|
|
|
/**
|
|
* Adapter for managing the `openid` scope.
|
|
*/
|
|
export interface JWTAdapter {
|
|
/**
|
|
* Issue a new ID token for user.
|
|
* @param user User data object obtained from the User adapter, must implement `OAuth2User` interface.
|
|
* @param scope String-list of scopes (usually used to determine the claims)
|
|
* @param nonce Cryptographic key passed to the authentication request, *must* be passed along as a claim.
|
|
*/
|
|
issueIdToken: (
|
|
user: OAuth2User,
|
|
client: OAuth2Client,
|
|
scope: string[],
|
|
nonce?: string
|
|
) => Promise<string>;
|
|
}
|
|
|
|
/**
|
|
* Render the OAuth2 decision page
|
|
*/
|
|
export type RenderOAuth2Decision = (
|
|
req: Request,
|
|
res: Response,
|
|
client: OAuth2Client,
|
|
scope: string[],
|
|
user: OAuth2User,
|
|
redirectUri: string
|
|
) => void;
|
|
|
|
/**
|
|
* OAuth2 adapter model
|
|
*/
|
|
export interface OAuth2AdapterModel {
|
|
accessToken: OAuth2AccessTokenAdapter;
|
|
refreshToken: OAuth2RefreshTokenAdapter;
|
|
user: OAuth2UserAdapter;
|
|
client: OAuth2ClientAdapter;
|
|
code: OAuth2CodeAdapter;
|
|
jwt?: JWTAdapter;
|
|
}
|
|
|
|
/**
|
|
* OAuth2 adapter
|
|
*/
|
|
export interface OAuth2 {
|
|
/**
|
|
* Adapter for the OAuth2 data models.
|
|
*/
|
|
model: OAuth2AdapterModel;
|
|
|
|
/**
|
|
* Logger wrapper, use a logger of your choice by calling `logger.setLogger(...)`.
|
|
* To disable, use `logger.setLogLevel('none')`.
|
|
*/
|
|
logger: OAuth2Logger;
|
|
|
|
/**
|
|
* Render function for the OAuth2 decision page
|
|
*/
|
|
decision: RenderOAuth2Decision;
|
|
}
|