Skip to content

Commit 669cbc5

Browse files
authored
Return codeVerifier value when both skipCodeExchange=true and usePKCE=true (#604)
* Create codeVerifier in RNAppAuthModule and pass it down AppAuth-Android. Return it in Authorize response only when `skipCodeExchange` == true. * codeVerifier already created in RNAppAuth when `usePKCE` == true. Added to `authorize` response conditionally when `skipCodeExchange` == true. * Update `AuthorizeResult` type to have optional `codeVerifier` property. * Update readme for `codeVerifier` that will now be returned if both `skipCodeExchange=true` and `usePKCE=true`
1 parent ae09747 commit 669cbc5

File tree

5 files changed

+55
-4
lines changed

5 files changed

+55
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ This is the result from the auth server:
146146
- **tokenType** - (`string`) the token type, e.g. Bearer
147147
- **scopes** - ([`string`]) the scopes the user has agreed to be granted
148148
- **authorizationCode** - (`string`) the authorization code (only if `skipCodeExchange=true`)
149+
- **codeVerifier** - (`string`) the codeVerifier value used for the PKCE exchange (only if both `skipCodeExchange=true` and `usePKCE=true`)
149150

150151
### `refresh`
151152

android/src/main/java/com/rnappauth/RNAppAuthModule.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import net.openid.appauth.ClientAuthentication;
4040
import net.openid.appauth.ClientSecretBasic;
4141
import net.openid.appauth.ClientSecretPost;
42+
import net.openid.appauth.CodeVerifierUtil;
4243
import net.openid.appauth.RegistrationRequest;
4344
import net.openid.appauth.RegistrationResponse;
4445
import net.openid.appauth.ResponseTypeValues;
@@ -63,6 +64,8 @@ public class RNAppAuthModule extends ReactContextBaseJavaModule implements Activ
6364
private Promise promise;
6465
private boolean dangerouslyAllowInsecureHttpRequests;
6566
private Boolean skipCodeExchange;
67+
private Boolean usePKCE;
68+
private String codeVerifier;
6669
private String clientAuthMethod = "basic";
6770
private Map<String, String> registrationRequestHeaders = null;
6871
private Map<String, String> authorizationRequestHeaders = null;
@@ -236,6 +239,7 @@ public void authorize(
236239
this.clientSecret = clientSecret;
237240
this.clientAuthMethod = clientAuthMethod;
238241
this.skipCodeExchange = skipCodeExchange;
242+
this.usePKCE = usePKCE;
239243

240244
// when serviceConfiguration is provided, we don't need to hit up the OpenID well-known id endpoint
241245
if (serviceConfiguration != null || hasServiceConfiguration(issuer)) {
@@ -408,7 +412,13 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode,
408412
}
409413

410414
if (this.skipCodeExchange) {
411-
WritableMap map = TokenResponseFactory.authorizationResponseToMap(response);
415+
WritableMap map;
416+
if (this.usePKCE && this.codeVerifier != null) {
417+
map = TokenResponseFactory.authorizationCodeResponseToMap(response, this.codeVerifier);
418+
} else {
419+
map = TokenResponseFactory.authorizationResponseToMap(response);
420+
}
421+
412422
if (promise != null) {
413423
promise.resolve(map);
414424
}
@@ -571,6 +581,9 @@ private void authorizeWithConfiguration(
571581

572582
if (!usePKCE) {
573583
authRequestBuilder.setCodeVerifier(null);
584+
} else {
585+
this.codeVerifier = CodeVerifierUtil.generateRandomCodeVerifier();
586+
authRequestBuilder.setCodeVerifier(this.codeVerifier);
574587
}
575588

576589
AuthorizationRequest authRequest = authRequestBuilder.build();

android/src/main/java/com/rnappauth/utils/TokenResponseFactory.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,27 @@ public static final WritableMap authorizationResponseToMap(AuthorizationResponse
8686

8787
return map;
8888
}
89+
90+
/*
91+
* Read raw authorization into a React Native map with codeVerifier value added if present to be passed down the bridge
92+
*/
93+
public static final WritableMap authorizationCodeResponseToMap(AuthorizationResponse authResponse, String codeVerifier) {
94+
WritableMap map = Arguments.createMap();
95+
map.putString("authorizationCode", authResponse.authorizationCode);
96+
map.putString("accessToken", authResponse.accessToken);
97+
map.putMap("additionalParameters", MapUtil.createAdditionalParametersMap(authResponse.additionalParameters));
98+
map.putString("idToken", authResponse.idToken);
99+
map.putString("tokenType", authResponse.tokenType);
100+
map.putArray("scopes", createScopeArray(authResponse.scope));
101+
102+
if (authResponse.accessTokenExpirationTime != null) {
103+
map.putString("accessTokenExpirationTime", DateUtil.formatTimestamp(authResponse.accessTokenExpirationTime));
104+
}
105+
106+
if (!TextUtils.isEmpty(codeVerifier)) {
107+
map.putString("codeVerifier", codeVerifier);
108+
}
109+
110+
return map;
111+
}
89112
}

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export interface AuthorizeResult {
8585
tokenType: string;
8686
scopes: string[];
8787
authorizationCode: string;
88+
codeVerifier?: string;
8889
}
8990

9091
export interface RefreshResult {

ios/RNAppAuth.m

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration
312312
[UIApplication.sharedApplication endBackgroundTask:taskId];
313313
taskId = UIBackgroundTaskInvalid;
314314
if (authorizationResponse) {
315-
resolve([self formatAuthorizationResponse:authorizationResponse]);
315+
resolve([self formatAuthorizationResponse:authorizationResponse withCodeVerifier:codeVerifier]);
316316
} else {
317317
reject([self getErrorCode: error defaultCode:@"authentication_failed"],
318318
[error localizedDescription], error);
@@ -378,21 +378,34 @@ - (void)refreshWithConfiguration: (OIDServiceConfiguration *)configuration
378378
/*
379379
* Take raw OIDAuthorizationResponse and turn it to response format to pass to JavaScript caller
380380
*/
381-
- (NSDictionary*)formatAuthorizationResponse: (OIDAuthorizationResponse*) response {
381+
- (NSDictionary *)formatAuthorizationResponse: (OIDAuthorizationResponse *) response withCodeVerifier: (NSString *) codeVerifier {
382382
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
383383
dateFormat.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
384384
[dateFormat setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
385385
[dateFormat setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
386386

387-
return @{@"authorizationCode": response.authorizationCode ? response.authorizationCode : @"",
387+
if (codeVerifier == nil) {
388+
return @{@"authorizationCode": response.authorizationCode ? response.authorizationCode : @"",
389+
@"state": response.state ? response.state : @"",
390+
@"accessToken": response.accessToken ? response.accessToken : @"",
391+
@"accessTokenExpirationDate": response.accessTokenExpirationDate ? [dateFormat stringFromDate:response.accessTokenExpirationDate] : @"",
392+
@"tokenType": response.tokenType ? response.tokenType : @"",
393+
@"idToken": response.idToken ? response.idToken : @"",
394+
@"scopes": response.scope ? [response.scope componentsSeparatedByString:@" "] : [NSArray new],
395+
@"additionalParameters": response.additionalParameters,
396+
};
397+
} else {
398+
return @{@"authorizationCode": response.authorizationCode ? response.authorizationCode : @"",
388399
@"state": response.state ? response.state : @"",
389400
@"accessToken": response.accessToken ? response.accessToken : @"",
390401
@"accessTokenExpirationDate": response.accessTokenExpirationDate ? [dateFormat stringFromDate:response.accessTokenExpirationDate] : @"",
391402
@"tokenType": response.tokenType ? response.tokenType : @"",
392403
@"idToken": response.idToken ? response.idToken : @"",
393404
@"scopes": response.scope ? [response.scope componentsSeparatedByString:@" "] : [NSArray new],
394405
@"additionalParameters": response.additionalParameters,
406+
@"codeVerifier": codeVerifier
395407
};
408+
}
396409
}
397410

398411
/*

0 commit comments

Comments
 (0)