import {Awaitable, hasOwnProperty, Maybe, TypeTag} from '../../util' import {Authenticatable, AuthenticatableIdentifier} from '../types' export enum OAuth2FlowType { code = 'code', } // export const oauth2FlowTypes: OAuth2FlowType[] = Object.entries(OAuth2FlowType).map(([_, value]) => value) export function isOAuth2FlowType(what: unknown): what is OAuth2FlowType { return [OAuth2FlowType.code].includes(what as any) } export interface OAuth2Client { id: string display: string secret: string allowedFlows: OAuth2FlowType[] allowedScopeIds: string[] allowedRedirectUris: string[] } export function isOAuth2Client(what: unknown): what is OAuth2Client { if ( typeof what !== 'object' || what === null ) { return false } if ( !hasOwnProperty(what, 'id') || !hasOwnProperty(what, 'display') || !hasOwnProperty(what, 'secret') || !hasOwnProperty(what, 'allowedFlows') || !hasOwnProperty(what, 'allowedScopeIds') || !hasOwnProperty(what, 'allowedRedirectUris') ) { return false } if ( typeof what.id !== 'string' || typeof what.display !== 'string' || typeof what.secret !== 'string' ) { return false } if ( !Array.isArray(what.allowedScopeIds) || !what.allowedScopeIds.every(x => typeof x === 'string') ) { return false } if ( !Array.isArray(what.allowedRedirectUris) || !what.allowedRedirectUris.every(x => typeof x === 'string') ) { return false } return !(!Array.isArray(what.allowedFlows) || !what.allowedFlows.every(x => isOAuth2FlowType(x))) } export abstract class ClientRepository { abstract find(id: string): Awaitable> } export interface OAuth2Scope { id: string name: string description?: string } export function isOAuth2Scope(what: unknown): what is OAuth2Scope { if ( typeof what !== 'object' || what === null ) { return false } if ( !hasOwnProperty(what, 'id') || !hasOwnProperty(what, 'name') ) { return false } if ( typeof what.id !== 'string' || typeof what.name !== 'string' ) { return false } return !hasOwnProperty(what, 'description') || typeof what.description === 'string' } export abstract class ScopeRepository { abstract find(id: string): Awaitable> abstract findByName(name: string): Awaitable> } export abstract class OAuth2Token { abstract id: string /** When undefined, these are client credentials. */ abstract userId?: AuthenticatableIdentifier abstract clientId: string abstract issued: Date abstract expires: Date abstract scope?: string } export type OAuth2TokenString = TypeTag<'@extollo/lib.OAuth2TokenString'> & string export function oauth2TokenString(s: string): OAuth2TokenString { return s as OAuth2TokenString } export function isOAuth2Token(what: unknown): what is OAuth2Token { if ( typeof what !== 'object' || what === null ) { return false } if ( !hasOwnProperty(what, 'id') || !hasOwnProperty(what, 'clientId') || !hasOwnProperty(what, 'issued') || !hasOwnProperty(what, 'expires') ) { return false } if ( typeof what.id !== 'string' || (hasOwnProperty(what, 'userId') && !(typeof what.userId === 'string' || typeof what.userId === 'number')) || typeof what.clientId !== 'string' || !(what.issued instanceof Date) || !(what.expires instanceof Date) ) { return false } return !hasOwnProperty(what, 'scope') || typeof what.scope === 'string' } export abstract class TokenRepository { abstract find(id: string): Awaitable> abstract issue(user: Authenticatable|undefined, client: OAuth2Client, scope?: string): Awaitable abstract decode(token: OAuth2TokenString): Awaitable> abstract encode(token: OAuth2Token): Awaitable } export interface OAuth2RedemptionCode { clientId: string userId: AuthenticatableIdentifier code: string scope?: string } export function isOAuth2RedemptionCode(what: unknown): what is OAuth2RedemptionCode { if ( typeof what !== 'object' || what === null ) { return false } if ( !hasOwnProperty(what, 'clientId') || !hasOwnProperty(what, 'userId') || !hasOwnProperty(what, 'code') ) { return false } if ( typeof what.clientId !== 'string' || !(typeof what.userId === 'number' || typeof what.userId === 'string') || typeof what.code !== 'string' ) { return false } return !hasOwnProperty(what, 'scope') || typeof what.scope === 'string' } export abstract class RedemptionCodeRepository { abstract find(code: string): Awaitable> abstract issue(user: Authenticatable, client: OAuth2Client, scope?: string): Awaitable }