oauth2flow.ts (1911B)
1 import { UUID } from "#core/domain/uuid.ts"; 2 import { Token } from "#core/domain/token.ts"; 3 import { Ephemeral } from "#core/domain/ephemeral.ts"; 4 import { Scope } from "#core/domain/scope.ts"; 5 import { DomainError } from "#core/domain/error.ts"; 6 import { 7 isCodeVerifier, 8 isSafeEqual, 9 nonceToken, 10 } from "#core/domain/crypto.ts"; 11 import { ACCESS_TOKEN_TTL, AUTH_CODE_TTL } from "#core/domain/constants.ts"; 12 13 export class OAuth2Flow { 14 constructor( 15 readonly id: UUID, 16 readonly clientId: UUID, 17 readonly scope: Scope, 18 readonly state: string | null = null, 19 private _resourceOwner: UUID | null = null, 20 private _token: Ephemeral<Token> = Ephemeral.empty(), 21 public version: number = 0, 22 ) {} 23 24 get resourceOwner() { 25 return this._resourceOwner; 26 } 27 28 get token() { 29 return this._token.valueOf(); 30 } 31 32 get expire() { 33 return this._token.expire; 34 } 35 36 get done() { 37 return this.version >= 3; 38 } 39 40 authorize(resourceOwner: UUID) { 41 if ( 42 this.done || 43 this.resourceOwner !== null || 44 !this._token.isExpired 45 ) { 46 throw new InvalidOAuth2Flow(); 47 } 48 49 this._resourceOwner = resourceOwner; 50 this._token = Ephemeral.of(nonceToken(), AUTH_CODE_TTL); 51 return this.token!; 52 } 53 54 accessToken(code: Token) { 55 if ( 56 this.done || 57 this.resourceOwner === null || 58 this._token.isExpired || 59 !isSafeEqual(this.token, code) 60 ) { 61 throw new InvalidOAuth2Flow(); 62 } 63 64 this._token = Ephemeral.of(nonceToken(), ACCESS_TOKEN_TTL); 65 return this.token!; 66 } 67 68 authenticate(accessToken: Token): void { 69 console.log(this, accessToken) 70 if ( 71 !this.done || !isSafeEqual(this.token, accessToken) || 72 this.resourceOwner === null 73 ) { 74 throw new InvalidOAuth2Flow(); 75 } 76 } 77 } 78 79 export class InvalidOAuth2Flow extends DomainError { 80 constructor() { 81 super("Invalid OAuth2 flow"); 82 } 83 }