From 368e8772578ae9446f142a66df1d66e85a83fea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Xalambr=C3=AD?= Date: Tue, 6 Aug 2024 00:45:49 -0500 Subject: [PATCH] Upgrade `@oslojs/oauth2` dependency (#108) Upgrade the doc to use the latest version of Oslo, copy parts of old versions of Oslo to this library to keep the compatibility while using the new version of Oslo --- bun.lockb | Bin 77188 -> 78652 bytes package.json | 23 ++---- src/index.ts | 56 ++++++------- src/lib/authorization-code.ts | 48 +++++++++++ src/lib/generator.ts | 21 +++++ src/lib/request.ts | 76 +++++++++++++++++ src/lib/token.ts | 150 ++++++++++++++++++++++++++++++++++ src/test/helpers.ts | 2 +- src/test/mock.ts | 3 +- tsconfig.json | 2 +- 10 files changed, 328 insertions(+), 53 deletions(-) create mode 100644 src/lib/authorization-code.ts create mode 100644 src/lib/generator.ts create mode 100644 src/lib/request.ts create mode 100644 src/lib/token.ts diff --git a/bun.lockb b/bun.lockb index 232ef294d4a6c83da7c8e724e3eaeedf0544fbed..fe11d981fc50b8d9a7ab85c8116ea2aaf7a9d72c 100755 GIT binary patch delta 14688 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 aUJ)#|^wCOiX=JiM)p(cL{>9y0o&E>U-1)iy delta 13833 zcmeI3d3;UR+Q;|a$%&khIpK&Si6Mdz$;q6siPpGLL7E8FL}VgFCL~2pj5Uj=hE53W zy{f38N>f^)RI9COwbal{OPeaKxmVtD-`}%`a&Hao=l*;5$M-zzx8`T9wb$O~WZCt- z9twcW0F5Cy&tRiy+- z0UuUP3N(EV=~lHn#8&7a2=0Pl&w%xSO@?g*n+WRzJ6RG0Gwg9kEni0y`H7+3Z#oHr zE24jbZ3tV^Kr3cs57y&LGegJbWfm1Y;jHBwJF2uOA8q$=rnzF9ie)Z>fPeOx`0#|y zEGQV0mn8@}(EiYAu-r@^J^cc@!o8e~y#$u?=j!RD zrFo-}T^L(Dkqvc{%oWX$fy=vWu$+H2Ec@~cEZ4UUh0xau`zi!@sHj@kJcIS*=Rs9sNRSnhBXEc#$y3vaxH z3N(m-6%oNm;0`p_Gaj{Q`had9<ahh^vky z-^Y{rdIo)n?31B+1PWn!D5s&fT)~sjUaiZ-8Y-mgzRT zpr|8Oe(te0TKVy)htua&?1}9J0h40Cj1KXPpM+(P z4#V=m=8enED#|M!rj3=y0m0+xHc1{JZs7NPZ2K?SbRWf?^| z+C z?R27YZ`tI~KoH_-ig$v!3qmnScA#3%7_%KJPF0CsG1A)&sN6@EoSdn~M>eP7nE-cD zQLRsmw8WX>eP!u0XDauV%@PK!Gtx}lW3vp!IUTu4^8{U?1J+p4Oy!MbsoqRAFsogt z9_Ew_#W#`7ZE$a4UhD!@HjXi;K|Kj2s#R`=N-)x-pBhrUMK*GekUuqdJ~zU8$z2EPdlj^-X0{FxGz}-ENv7mAX;6pDb;0 zqZ*i9?o{t5n}a=&jH13&=@}#CdQf>Y*}MXgTsi!6k1;!9i(ucKNc4&^_tlk2RkNVj z2?y1QQ&2LLISW%jWUe5e6c^K#5Ia%zL07hjOAsIOOO{H371@ z8}40hT%=k{j5N!a;sa&#r-S4ilC+_rZ}w%xMoOuB8DNFwcR~=KJA2GS?zw zR}|BnSF}Oqaw0j zW-B%;eT<;?>B@#)R>#5Q&M*5@~)d$OFXFMT!<*7?Y>SLwywz7Gl9*NcE9b-NW)dN`s z_1^VqE(rb9E@73J(xGB`HJDZ))SIUGCYa736iHp&63jmShBH{fQeS^6Z!b#={HX?} z)}QKOgaC>U!3GsT1s3t@DT9#n5t@vLou>SZXYQA?`nD4U136okhRiA@Muw%)5Gu@#kv%2IMGs)1SAit1t9 zTT^@|+4O8{tXt~hm08z!-BAJb1&j4qzJ_5=8M~SXqeV?FIV~R@RSD-L))O z&mnDoUp*+E^}FIVxC)9_H%7T*jPz|AstK2+z+kF}sR*Wcn{2ujEC{J;Ka$!Slfbva z%TNPpw||1^5<)}hc6fq$SUW)&t~QJu(vEgi6Cq2#wWIn7+4Mwv3@T0WOfWA(h#SSd zfd%{l6i+nPS3r!pC!XHy2!+IY?+g-GMC=aT?nh66ez1M-_ zqh-^(9R*=1b%{SEoZ{nT^J|D4p!SM~;@5DhiIYuFVwb_A$v46L zIzl6mtlfX+2xCM<_ZU+mRA+U>ry!)=dw6O{J0d7PK{ktMxwBdnPatVyZ@SSvE&SY5N+wiGj|6%0?D#FaHFJUC@+>yPp-FamX13g+avxiBL13wB^T- zu1}%x4^xter=cS@Q9TXQ)g6l09iBy84vL4&g%YD<%=@8uk3|c}`70Ff3hGX6j=^f+ z2cQdyc#6hSO;_3Mg-3Oonx7X!J{0?GQrGFaI4bWZOMdZG12Z6=>bu2PcnRwJpdi9} z1HSHOIo$`C0JcQ+V(SR7hp5-XESL8OY_Vgg7i@(Q_}w}ogd0)4*h&ERl3EaVjB0Ve zs25wV5ch(><*>fg>wcE=VR5O~U5obdX2p{z;5~GG9MxElzt?hlQ%MvDA+EF^%9}i&Od;oF zS1Nr{5q+r^Y9~}siXt|l!ju%sebSY_glbAHQd7v1;z~196|or|fjS5kHB=FsQ{~VU znw08F=b-{9JS~OV4t1raX^Pl_&O#lB>M=~g*Yc`iDfDcbE8Tz!qQv3wZy5X=u86^O z1*#4z`6)$gN7YZkzv1w2gd&De(g^tX6#RqgNaEA*4=U?vMeIadpw^9mf9Z-CP8sR& z?`bzWklt5}AkPf=mk$3j6fufwp>{$Aja0-KDjW&_GTbt({L6=bP(#&TTgGa=9;WvCAXHR= zB0fcx1@Lbi{DXR$!k6`d~WK9<2ps0s>y26I>r|DI9AN;(U59ID4;MVwAmli}Ypm_w+UlsEiueX?fm$~gKFwFe zEtD}IKFxzqP}|6J0eqSdpB5(MtV$7U=`7T7s2)ocaUWGJ!JJiL&Y<>FB4N&! zV9rPpKcFj6bx_IADdLAz{T$|uFlS4N>Xt@IaT572JK$V_S6_Uwv{Rw04{dgJbSWtt zpEar=Q$V`g@@XE%U%Rj2{S9x>_HC8V4-x75id<{1n^sbPB~ahDUPW9ZE!T8svb&nO zN{^9w1^gE_;jIqZUyN|4t>y&UwZ_>hw_2ykON0C%_{m=5^Dw_-IP=0KPQxUCxFiz>!Qn5{>? z1IvXmU&2Db*KWX#xP!5PJ7s*k_CT1w)^P{k1Dwwj@b3y-?;gN${IR zMSvUI3pkR$b&Uhu*gn8V$K556;Mmyh=E3^C5iTI{qn5J)FkBzX~>T{j5m^FNW*jd+-CO13!Wb z;3seqTmr9vSHa)F-@!V-i)KB*>J*|t42T7BU?q4N3;<7nfglMC0)s&^Na2N(ioj5S zpDu-AU^sXRi~s>35VQa-K`X%H-v!8^E9eHA!9^?Z2Z5j%aV3DizB_^&2>%LhfqK9{ zRD1)@^1}HRf$zX3z_-aNup02MLUvFICV?`L4l=+9S8z# zKo9U3;Gf4zK@P}8`$8UYLN6NtXTZ0_9mM?$@Z^bL3xems8ZaHq1kZvwU@n*k=7WX6 zAG8HM!9*|`@DCA9fCcccA6pT(4OBdb4}PzG6Lve``)~)?3HbM&li+Q@x6>}bEAJrq z5PSr9UA+sAfdgPK;FbL`I06Dt_f6Q1;3dHKb~Wq_FpCFqHUeV-|8&ClAO9lv0`v;7 z6f6ULYmEbZ>+r3z9I!0}98Wq$dK_El!}!28{|l`6mgM+_M@1kcuUnpBo`n{GvyKM^ zARGh&FJJ2bu!3g5PbUnPXOue}4B7x*R;@uR&=PP*IF0jk0->NiXa_hgL{PM~$&n#| z*D6~s#08AZtU1h+G6p1rAs`9xUCfO?L6t|nD|kge4tP~}2R6V>@$K0eB!GAj1tLKN zhy$@88pHrj_+#q*=Eq8`JTnP*>Uqbjr z@B(-l*s1m_?~1hutpU|wHP{dOfNkJ?@H$uzUIY8UUa%c-`bMw;a2^Hl%Ge6FfX!eN zcmwbRz6rSATCfYe1K#F!@D>6)L1&Nv?p>lgpt&%w1{u_V_rM;o8@vlxe*``R+{uIB z18@NB`2ruf{I1aYV{IG|+IINZYa(sxf9vPd}YnSRn{HC}LkyS4Yt^fI5fIeTPiIP$9v`_jIXeOlX`w6iJ3 znb1M!Z&ts2?7c{})v!?9zBipH?35+?!5NHWq;1ar=6<{Vw{}TvuZ{)YI=DamB%B-l z_>@h0!g*l(D^_A0jNgRk69_|O1(VbAcVzV0OTc5Ec)aT6}lIhvs=YsGKF*;Q8Nxzs(V^jy#yQcc4Zj{yYT4f#DK>Sk1$~Hzy3@@w{jA2h zRC!w7rI#`~NXQq7Ca{H1a;L!amT2Q3>s##{hqXL0?JjyvtSW|7=IX!5rErDlPeR=RJ=jd#vWPad>w_LHL{bKWDB)ias3m9&`sqyLqY)UwfMP(#f8` zuELy+h(b|}q&p_A5$5i8zPNdO%g0Y31wQadYDFaxXe*{4sZg2P8%Kn*sn%WIp^%5xU$eWzGm9op1Mv)$?9Ig6yv#>3|mBVn8nUY~4l zwZ2!UnP@Nu&S3P8T4>yPi=>%>#(~`dy=Gl+;_o&H`OqpmZJfoe>Xf#i&Gs&Fl9=06 zyNis&zuB{@E;PHbyA);gshi)FE}yqqjpM|xB)_u$`9o=$s<&~x%wB0q^7l5YaUMBj z(Tl>ymP_{`1BR1laA#AR|Gh=JJCS>!{@O%ZA3Tw7_|fnmEY?TOr`0&-T<7qH8HZ&?UJPd=+^-Ef7bf(Tvx)eaa)mf}!p!S2t=8a{oPw%`r zOC5W4Df$Fb*pK>dYq7RNKHMhu7k7p1kju8jt0S!DiwmTw$fpF-iXUy#xj_2(M~n6M zgVh&n{PqOiKB77t%@gv^7E3N**|nq%KU*ZdjT*ovlIuvv{G${yFIdo*cN z5T!PyE}@gH=(7u!XyZ)t`Y%)0ADFk9`^-01#NFf9sT+o0ZIM6dsGfp_lGd91f3is9 zTT|>${jA3E?7^9CUxmChVu#)Y@_pKxR{vy)HqL2Ze4|UX&-UcDcbhOyXb1IgvHJb4 zr@Yk^ELT3uP2Db8tj1C9=GNO4RqKEFP_G+zidHwpU-Xl-ywb5YH2xwUY{7I2B_CDw z{d)NjFsKb` zU6z&w(alS?=)`u~Y4dj`T}V3K^LAI=XAFvQApSpQBs@|mYfU45wpfjm?OjJ)olyVS zsttNkOof&~Un&n-sEtDC%-yx5z8zSk)*%%7i$#hIq29mPB)1MU8A@Ge*bF+*=3gw) z#)DrO*pr~=k{rs(nG3Q!2NOveyxWAqhUv~j$B|A)POmPMcGg6#Y(4-Xd}Tv2K` zt-NB9MuyYQEB&m-|1#;ZFEjI#;A=&w0q=>tq9be+c-103W~2D4HmSdj#$B~U8wc>e z+IC`|P~^@JFMY3d_ju5A^jZY%LJi9z>GIWn5>DL9*RucAqyNM;_0jKk{Q-|%<0;LL zUG*t#lQN=dF|r%y_6N=RYLkCzUl(+f-(7G=ltt6~*W0`7#p^pi^|k!c_E_?|(J$3F zuHSUQ1=%}<%*+}oiBX{u>T)rT{6ANH`Ebug4aT4; zezU*;=3YS|Ty3w?TQ|DX%^NnUT%`6lW!?Cdi&`gU#72EAPI9}GdQ~fMvBTQEH$^)0 M>*{TDZ*7hHe>>UckpKVy diff --git a/package.json b/package.json index 9e4ccf7..c96d324 100644 --- a/package.json +++ b/package.json @@ -3,16 +3,8 @@ "version": "2.1.0", "description": "A strategy to use and implement OAuth2 framework for authentication with federated services like Google, Facebook, GitHub, etc.", "license": "MIT", - "funding": [ - "https://github.com/sponsors/sergiodxa" - ], - "keywords": [ - "remix", - "remix-auth", - "auth", - "authentication", - "strategy" - ], + "funding": ["https://github.com/sponsors/sergiodxa"], + "keywords": ["remix", "remix-auth", "auth", "authentication", "strategy"], "author": { "name": "Sergio Xalambrí", "email": "hello+oss@sergiodxa.com", @@ -31,23 +23,22 @@ "typecheck": "tsc --noEmit", "quality": "biome check .", "quality:fix": "biome check . --apply-unsafe", - "exports": "bun run ./scripts/exports.ts" + "exports": "bun run ./scripts/exports.ts", + "unused": "knip" }, "sideEffects": false, "type": "module", "engines": { "node": "^18.0.0 || ^20.0.0 || >=20.0.0" }, - "files": [ - "build", - "package.json", - "README.md" - ], + "files": ["build", "package.json", "README.md"], "exports": { ".": "./build/index.js", "./package.json": "./package.json" }, "dependencies": { + "@oslojs/crypto": "^0.6.2", + "@oslojs/encoding": "^0.4.1", "@oslojs/oauth2": "^0.5.0", "debug": "^4.3.4" }, diff --git a/src/index.ts b/src/index.ts index c83adaa..b0c958c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,3 @@ -import { - AuthorizationCodeAccessTokenRequestContext, - AuthorizationCodeAuthorizationURL, - OAuth2RequestError, - RefreshRequestContext, - TokenResponseBody, - TokenRevocationRequestContext, - generateCodeVerifier, - generateState, - sendTokenRequest, - sendTokenRevocationRequest, -} from "@oslojs/oauth2"; import { AppLoadContext, SessionStorage, @@ -21,6 +9,10 @@ import { Strategy, StrategyVerifyCallback, } from "remix-auth"; +import { AuthorizationCode } from "./lib/authorization-code.js"; +import { Generator } from "./lib/generator.js"; +import { OAuth2Request } from "./lib/request.js"; +import { Token } from "./lib/token.js"; let debug = createDebug("OAuth2Strategy"); @@ -103,7 +95,7 @@ export interface OAuth2StrategyVerifyParams< Profile extends OAuth2Profile, ExtraTokenParams extends Record = Record, > { - tokens: TokenResponseBody & ExtraTokenParams; + tokens: Token.Response.Body & ExtraTokenParams; profile: Profile; request: Request; context?: AppLoadContext; @@ -168,17 +160,17 @@ export class OAuth2Strategy< if (!stateUrl) { debug("No state found in the URL, redirecting to authorization endpoint"); - let state = generateState(); + let state = Generator.state(); session.set(this.sessionStateKey, state); debug("State", state); - let codeVerifier = generateCodeVerifier(); + let codeVerifier = Generator.codeVerifier(); session.set(this.sessionCodeVerifierKey, codeVerifier); debug("Code verifier", codeVerifier); - let authorizationURL = new AuthorizationCodeAuthorizationURL( + let authorizationURL = new AuthorizationCode.AuthorizationURL( this.options.authorizationEndpoint.toString(), this.options.clientId, ); @@ -187,7 +179,7 @@ export class OAuth2Strategy< authorizationURL.setState(state); if (this.options.scopes) - authorizationURL.appendScopes(...this.options.scopes); + authorizationURL.addScopes(...this.options.scopes); if (this.options.codeChallengeMethod === "S256") { authorizationURL.setS256CodeChallenge(codeVerifier); @@ -265,7 +257,7 @@ export class OAuth2Strategy< try { debug("Validating authorization code"); - let context = new AuthorizationCodeAccessTokenRequestContext(code); + let context = new Token.Request.Context(code); context.setRedirectURI(this.options.redirectURI.toString()); context.setCodeVerifier(codeVerifier); @@ -282,7 +274,7 @@ export class OAuth2Strategy< ); } - let tokens = await sendTokenRequest( + let tokens = await Token.Request.send( this.options.tokenEndpoint.toString(), context, { signal: request.signal }, @@ -334,7 +326,7 @@ export class OAuth2Strategy< } } - protected async userProfile(tokens: TokenResponseBody): Promise { + protected async userProfile(tokens: Token.Response.Body): Promise { return { provider: "oauth2" } as Profile; } @@ -349,7 +341,7 @@ export class OAuth2Strategy< */ protected authorizationParams( params: URLSearchParams, - request?: Request, + request: Request, ): URLSearchParams { return new URLSearchParams(params); } @@ -368,9 +360,9 @@ export class OAuth2Strategy< ) { let scopes = options.scopes ?? this.options.scopes ?? []; - let context = new RefreshRequestContext(refreshToken); + let context = new Token.RefreshRequest.Context(refreshToken); - context.appendScopes(...scopes); + context.addScopes(...scopes); if (this.options.authenticateWith === "http_basic_auth") { context.authenticateWithHTTPBasicAuth( @@ -384,14 +376,14 @@ export class OAuth2Strategy< ); } - return sendTokenRequest( + return Token.Request.send( this.options.tokenEndpoint.toString(), context, { signal: options.signal }, ); } - public revokeToken( + public async revokeToken( token: string, options: { signal?: AbortSignal; @@ -402,11 +394,9 @@ export class OAuth2Strategy< throw new Error("Token revocation endpoint is not set"); } - let context = new TokenRevocationRequestContext(token); + let context = new Token.RevocationRequest.Context(token); - if (options.tokenType) { - context.setTokenTypeHint(options.tokenType); - } + if (options.tokenType) context.setTokenTypeHint(options.tokenType); if (this.options.authenticateWith === "http_basic_auth") { context.authenticateWithHTTPBasicAuth( @@ -420,8 +410,8 @@ export class OAuth2Strategy< ); } - return sendTokenRevocationRequest( - this.options.tokenRevocationEndpoint.toString(), + await Token.RevocationRequest.send( + this.options.tokenRevocationEndpoint, context, { signal: options.signal }, ); @@ -449,5 +439,5 @@ export class OAuth2Error extends Error { } } -export { OAuth2RequestError }; -export type { TokenResponseBody } from "@oslojs/oauth2"; +export const OAuth2RequestError = OAuth2Request.Error; +export type TokenResponseBody = Token.Response.Body; diff --git a/src/lib/authorization-code.ts b/src/lib/authorization-code.ts new file mode 100644 index 0000000..1751324 --- /dev/null +++ b/src/lib/authorization-code.ts @@ -0,0 +1,48 @@ +/** + * A lot of the code here was originally implemented by @pilcrowOnPaper for a + * previous version of `@oslojs/oauth2`, as Pilcrow decided to change the + * direction of the library to focus on response parsing, I decided to copy the + * old code and adapt it to the new structure of the library. + */ +import { sha256 } from "@oslojs/crypto/sha2"; +import { encodeBase64urlNoPadding } from "@oslojs/encoding"; + +export namespace AuthorizationCode { + export class AuthorizationURL extends URL { + constructor(authorizationEndpoint: string, clientId: string) { + super(authorizationEndpoint); + this.searchParams.set("response_type", "code"); + this.searchParams.set("client_id", clientId); + } + + public setRedirectURI(redirectURI: string): void { + this.searchParams.set("redirect_uri", redirectURI); + } + + public addScopes(...scopes: string[]): void { + if (scopes.length < 1) { + return; + } + let scopeValue = scopes.join(" "); + const existingScopes = this.searchParams.get("scope"); + if (existingScopes !== null) scopeValue = ` ${existingScopes}`; + this.searchParams.set("scope", scopeValue); + } + + public setState(state: string): void { + this.searchParams.set("state", state); + } + + public setS256CodeChallenge(codeVerifier: string): void { + const codeChallengeBytes = sha256(new TextEncoder().encode(codeVerifier)); + const codeChallenge = encodeBase64urlNoPadding(codeChallengeBytes); + this.searchParams.set("code_challenge", codeChallenge); + this.searchParams.set("code_challenge_method", "S256"); + } + + public setPlainCodeChallenge(codeVerifier: string): void { + this.searchParams.set("code_challenge", codeVerifier); + this.searchParams.set("code_challenge_method", "plain"); + } + } +} diff --git a/src/lib/generator.ts b/src/lib/generator.ts new file mode 100644 index 0000000..e4fd70a --- /dev/null +++ b/src/lib/generator.ts @@ -0,0 +1,21 @@ +/** + * A lot of the code here was originally implemented by @pilcrowOnPaper for a + * previous version of `@oslojs/oauth2`, as Pilcrow decided to change the + * direction of the library to focus on response parsing, I decided to copy the + * old code and adapt it to the new structure of the library. + */ +import { encodeBase64urlNoPadding } from "@oslojs/encoding"; + +export namespace Generator { + export function codeVerifier(): string { + const randomValues = new Uint8Array(32); + crypto.getRandomValues(randomValues); + return encodeBase64urlNoPadding(randomValues); + } + + export function state(): string { + const randomValues = new Uint8Array(32); + crypto.getRandomValues(randomValues); + return encodeBase64urlNoPadding(randomValues); + } +} diff --git a/src/lib/request.ts b/src/lib/request.ts new file mode 100644 index 0000000..1e0d56a --- /dev/null +++ b/src/lib/request.ts @@ -0,0 +1,76 @@ +/** + * A lot of the code here was originally implemented by @pilcrowOnPaper for a + * previous version of `@oslojs/oauth2`, as Pilcrow decided to change the + * direction of the library to focus on response parsing, I decided to copy the + * old code and adapt it to the new structure of the library. + */ +import { encodeBase64 } from "@oslojs/encoding"; + +export namespace OAuth2Request { + export abstract class Context { + public method: string; + public body = new URLSearchParams(); + public headers = new Headers(); + + constructor(method: string) { + this.method = method; + this.headers.set("Content-Type", "application/x-www-form-urlencoded"); + this.headers.set("Accept", "application/json"); + this.headers.set("User-Agent", "oslo"); + } + + public setClientId(clientId: string): void { + this.body.set("client_id", clientId); + } + + public authenticateWithRequestBody( + clientId: string, + clientSecret: string, + ): void { + this.setClientId(clientId); + this.body.set("client_secret", clientSecret); + } + + public authenticateWithHTTPBasicAuth( + clientId: string, + clientSecret: string, + ): void { + const authorizationHeader = `Basic ${encodeBase64( + new TextEncoder().encode(`${clientId}:${clientSecret}`), + )}`; + this.headers.set("Authorization", authorizationHeader); + } + + toRequest(url: ConstructorParameters["0"]) { + return new Request(url, { + method: this.method, + body: this.body, + headers: this.headers, + }); + } + } + + // biome-ignore lint/suspicious/noShadowRestrictedNames: It's namespaced + export class Error extends globalThis.Error { + public request: Request; + public context: OAuth2Request.Context; + public description: string | null; + public uri: string | null; + public responseHeaders: Headers; + + constructor( + message: string, + request: Request, + context: OAuth2Request.Context, + responseHeaders: Headers, + options?: { description?: string; uri?: string }, + ) { + super(message); + this.request = request; + this.context = context; + this.responseHeaders = responseHeaders; + this.description = options?.description ?? null; + this.uri = options?.uri ?? null; + } + } +} diff --git a/src/lib/token.ts b/src/lib/token.ts new file mode 100644 index 0000000..a4f6b51 --- /dev/null +++ b/src/lib/token.ts @@ -0,0 +1,150 @@ +/** + * A lot of the code here was originally implemented by @pilcrowOnPaper for a + * previous version of `@oslojs/oauth2`, as Pilcrow decided to change the + * direction of the library to focus on response parsing, I decided to copy the + * old code and adapt it to the new structure of the library. + */ +import { OAuth2RequestResult, TokenRequestResult } from "@oslojs/oauth2"; +import { OAuth2Request } from "./request.js"; + +type URLConstructor = ConstructorParameters[0]; + +export namespace Token { + export namespace Response { + export interface Body { + access_token: string; + token_type: string; + expires_in?: number; + refresh_token?: string; + scope?: string; + } + + export interface ErrorBody { + error: string; + error_description?: string; + } + } + + export namespace Request { + export class Context extends OAuth2Request.Context { + constructor(authorizationCode: string) { + super("POST"); + this.body.set("grant_type", "authorization_code"); + this.body.set("code", authorizationCode); + } + + public setCodeVerifier(codeVerifier: string): void { + this.body.set("code_verifier", codeVerifier); + } + + public setRedirectURI(redirectURI: string): void { + this.body.set("redirect_uri", redirectURI); + } + } + + export async function send>( + endpoint: URLConstructor, + context: OAuth2Request.Context, + options?: { signal?: AbortSignal }, + ): Promise { + let request = context.toRequest(endpoint); + let response = await fetch(request, { signal: options?.signal }); + let body = await response.json(); + + let result = new Result(body); + + if (result.hasErrorCode()) { + throw new OAuth2Request.Error( + result.errorCode(), + request, + context, + response.headers, + { + description: result.errorDescription(), + uri: result.errorURI(), + }, + ); + } + + return result.toJSON(); + } + + export class Result< + ExtraParams extends Record, + > extends TokenRequestResult { + toJSON(): Response.Body & ExtraParams { + return { + ...this.body, + access_token: this.accessToken(), + token_type: this.tokenType(), + expires_in: this.accessTokenExpiresInSeconds(), + scope: this.scopes().join(" "), + refresh_token: this.refreshToken(), + } as Response.Body & ExtraParams; + } + } + } + + export namespace RevocationRequest { + export class Context extends OAuth2Request.Context { + constructor(token: string) { + super("POST"); + this.body.set("token", token); + } + + public setTokenTypeHint( + tokenType: "access_token" | "refresh_token", + ): void { + if (tokenType === "access_token") { + this.body.set("token_type_hint", "access_token"); + } else if (tokenType === "refresh_token") { + this.body.set("token_type_hint", "refresh_token"); + } + } + } + + export async function send( + endpoint: URLConstructor, + context: OAuth2Request.Context, + options?: { signal?: AbortSignal }, + ) { + let request = context.toRequest(endpoint); + let response = await fetch(request, { signal: options?.signal }); + let body = await response.json(); + + let result = new OAuth2RequestResult(body); + + if (result.hasErrorCode()) { + throw new OAuth2Request.Error( + result.errorCode(), + request, + context, + response.headers, + { description: result.errorDescription(), uri: result.errorURI() }, + ); + } + } + } + + export namespace RefreshRequest { + export class Context extends OAuth2Request.Context { + constructor(refreshToken: string) { + super("POST"); + this.body.set("grant_type", "refresh_token"); + this.body.set("refresh_token", refreshToken); + } + + public addScopes(...scopes: string[]): void { + if (scopes.length < 1) { + return; + } + let scopeValue = scopes.join(" "); + const existingScopes = this.body.get("scope"); + if (existingScopes !== null) { + scopeValue = `${scopeValue} ${existingScopes}`; + } + this.body.set("scope", scopeValue); + } + } + } +} diff --git a/src/test/helpers.ts b/src/test/helpers.ts index e79c7cb..fb49b01 100644 --- a/src/test/helpers.ts +++ b/src/test/helpers.ts @@ -1,4 +1,4 @@ -export function isResponse(value: unknown): value is Response { +function isResponse(value: unknown): value is Response { return value instanceof Response; } diff --git a/src/test/mock.ts b/src/test/mock.ts index bd3b469..295556e 100644 --- a/src/test/mock.ts +++ b/src/test/mock.ts @@ -1,6 +1,5 @@ import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; -import { TokenResponseBody } from "oslo/oauth2"; export const server = setupServer( http.post("https://example.app/token", async () => { @@ -10,6 +9,6 @@ export const server = setupServer( refresh_token: "mocked", scope: ["user:email", "user:profile"].join(" "), token_type: "Bearer", - } satisfies TokenResponseBody); + }); }), ); diff --git a/tsconfig.json b/tsconfig.json index 2a017f8..a03f335 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,5 +12,5 @@ "outDir": "./build" }, "exclude": ["node_modules"], - "include": ["src/index.ts"] + "include": ["src/index.ts", "src/lib/**/*.ts"] }