You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
4.9 KiB
180 lines
4.9 KiB
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<Maybe<OAuth2Client>>
|
|
}
|
|
|
|
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<Maybe<OAuth2Scope>>
|
|
|
|
abstract findByName(name: string): Awaitable<Maybe<OAuth2Scope>>
|
|
}
|
|
|
|
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<Maybe<OAuth2Token>>
|
|
|
|
abstract issue(user: Authenticatable|undefined, client: OAuth2Client, scope?: string): Awaitable<OAuth2Token>
|
|
|
|
abstract decode(token: OAuth2TokenString): Awaitable<Maybe<OAuth2Token>>
|
|
|
|
abstract encode(token: OAuth2Token): Awaitable<OAuth2TokenString>
|
|
}
|
|
|
|
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<Maybe<OAuth2RedemptionCode>>
|
|
|
|
abstract issue(user: Authenticatable, client: OAuth2Client, scope?: string): Awaitable<OAuth2RedemptionCode>
|
|
}
|