@@ -22,6 +22,7 @@ import { messages } from './messages'
2222import { Display } from './types'
2323import {
2424 authWrite ,
25+ exchangeForAuthorizationCode ,
2526 formOAuthResponse ,
2627 getDeveloperApp ,
2728 getIsAppAuthorized ,
@@ -44,13 +45,24 @@ const useParsedQueryParams = () => {
4445 redirect_uri : redirectUri ,
4546 app_name : appName ,
4647 response_mode : responseMode ,
47- api_key : apiKey ,
48+ api_key,
49+ client_id,
4850 origin,
4951 tx,
5052 display : displayQueryParam ,
53+ response_type : responseType ,
54+ code_challenge : codeChallenge ,
55+ code_challenge_method : codeChallengeMethod ,
5156 ...rest
5257 } = queryString . parse ( search )
5358
59+ const apiKey =
60+ typeof api_key === 'string'
61+ ? api_key
62+ : typeof client_id === 'string'
63+ ? client_id
64+ : undefined
65+
5466 const parsedRedirectUri = useMemo < 'postmessage' | URL | null > ( ( ) => {
5567 if ( redirectUri && typeof redirectUri === 'string' ) {
5668 if ( redirectUri . toLowerCase ( ) === 'postmessage' ) {
@@ -112,6 +124,14 @@ const useParsedQueryParams = () => {
112124 } else if ( ! isValidApiKey ( apiKey ) ) {
113125 error = messages . invalidApiKeyError
114126 }
127+ // PKCE-specific validations when response_type=code
128+ if ( ! error && responseType === 'code' ) {
129+ if ( ! codeChallenge || typeof codeChallenge !== 'string' ) {
130+ error = messages . missingCodeChallengeError
131+ } else if ( codeChallengeMethod !== 'S256' ) {
132+ error = messages . invalidCodeChallengeMethodError
133+ }
134+ }
115135 } else if ( scope === 'write_once' ) {
116136 // Write-once scope-specific validations:
117137 const { error : writeOnceParamsError , txParams : txParamsRes } =
@@ -148,7 +168,10 @@ const useParsedQueryParams = () => {
148168 error,
149169 tx,
150170 txParams,
151- display
171+ display,
172+ responseType,
173+ codeChallenge,
174+ codeChallengeMethod
152175 }
153176}
154177
@@ -186,6 +209,9 @@ export const useOAuthSetup = ({
186209 txParams,
187210 tx,
188211 display,
212+ responseType,
213+ codeChallenge,
214+ codeChallengeMethod,
189215 error : initError
190216 } = useParsedQueryParams ( )
191217 const { data : accountStatus } = useAccountStatus ( )
@@ -503,6 +529,59 @@ export const useOAuthSetup = ({
503529 }
504530 }
505531
532+ // PKCE flow: exchange for authorization code and redirect with code
533+ if ( responseType === 'code' ) {
534+ const code = await exchangeForAuthorizationCode ( {
535+ account,
536+ userEmail,
537+ apiKey : apiKey as string ,
538+ redirectUri : ( redirectUri as string ) ?? 'postMessage' ,
539+ codeChallenge : codeChallenge as string ,
540+ codeChallengeMethod : ( codeChallengeMethod as string ) ?? 'S256' ,
541+ scope : scope as string ,
542+ onError : ( ) => {
543+ onError ( {
544+ isUserError : false ,
545+ errorMessage : messages . miscError
546+ } )
547+ }
548+ } )
549+ if ( ! code ) return
550+
551+ record (
552+ make ( Name . AUDIUS_OAUTH_COMPLETE , {
553+ appId : ( apiKey || appName ) ! ,
554+ scope : scope ! ,
555+ alreadyAuthorized : ! shouldCreateWriteGrant
556+ } )
557+ )
558+
559+ if ( parsedRedirectUri === 'postmessage' ) {
560+ if ( parsedOrigin && window . opener ) {
561+ window . opener . postMessage ( { state, code } , parsedOrigin . origin )
562+ } else {
563+ onError ( {
564+ isUserError : false ,
565+ errorMessage : messages . noWindowError
566+ } )
567+ }
568+ } else if ( parsedRedirectUri ) {
569+ if ( responseMode === 'query' ) {
570+ if ( state != null ) {
571+ parsedRedirectUri . searchParams . append ( 'state' , state as string )
572+ }
573+ parsedRedirectUri . searchParams . append ( 'code' , code )
574+ window . location . href = parsedRedirectUri . toString ( )
575+ } else {
576+ const statePart = state != null ? `state=${ state } &` : ''
577+ parsedRedirectUri . hash = `#${ statePart } code=${ code } `
578+ window . location . href = parsedRedirectUri . toString ( )
579+ }
580+ }
581+ return
582+ }
583+
584+ // Implicit flow: form JWT and redirect
506585 await formResponseAndRedirect ( {
507586 account,
508587 grantCreated : shouldCreateWriteGrant
0 commit comments