Skip to content

Commit d94dd7e

Browse files
louisaspicerJesus Hernandez
authored andcommitted
Support manual code exchange
1 parent 9000c28 commit d94dd7e

File tree

5 files changed

+82
-8
lines changed

5 files changed

+82
-8
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class RNAppAuthModule extends ReactContextBaseJavaModule implements Activ
6161
private final ReactApplicationContext reactContext;
6262
private Promise promise;
6363
private Boolean dangerouslyAllowInsecureHttpRequests;
64+
private Boolean skipCodeExchange;
6465
private String clientAuthMethod = "basic";
6566
private Map<String, String> registrationRequestHeaders = null;
6667
private Map<String, String> authorizationRequestHeaders = null;
@@ -215,6 +216,7 @@ public void authorize(
215216
final ReadableArray scopes,
216217
final ReadableMap additionalParameters,
217218
final ReadableMap serviceConfiguration,
219+
final Boolean skipCodeExchange,
218220
final Boolean usePKCE,
219221
final String clientAuthMethod,
220222
final Boolean dangerouslyAllowInsecureHttpRequests,
@@ -232,6 +234,7 @@ public void authorize(
232234
this.additionalParametersMap = additionalParametersMap;
233235
this.clientSecret = clientSecret;
234236
this.clientAuthMethod = clientAuthMethod;
237+
this.skipCodeExchange = skipCodeExchange;
235238

236239
// when serviceConfiguration is provided, we don't need to hit up the OpenID well-known id endpoint
237240
if (serviceConfiguration != null || mServiceConfiguration.get() != null) {
@@ -391,6 +394,13 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode,
391394
return;
392395
}
393396

397+
if (this.skipCodeExchange) {
398+
WritableMap map = TokenResponseFactory.authorizationResponseToMap(response);
399+
promise.resolve(map);
400+
return;
401+
}
402+
403+
394404
final Promise authorizePromise = this.promise;
395405
final AppAuthConfiguration configuration = createAppAuthConfiguration(
396406
createConnectionBuilder(this.dangerouslyAllowInsecureHttpRequests, this.tokenRequestHeaders)

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,26 @@ public static final WritableMap tokenResponseToMap(TokenResponse response, Autho
6464
}
6565

6666

67+
return map;
68+
}
69+
70+
71+
/*
72+
* Read raw authorization into a React Native map to be passed down the bridge
73+
*/
74+
public static final WritableMap authorizationResponseToMap(AuthorizationResponse authResponse) {
75+
WritableMap map = Arguments.createMap();
76+
map.putString("authorizationCode", authResponse.authorizationCode);
77+
map.putString("accessToken", authResponse.accessToken);
78+
map.putMap("additionalParameters", MapUtil.createAdditionalParametersMap(authResponse.additionalParameters));
79+
map.putString("idToken", authResponse.idToken);
80+
map.putString("tokenType", authResponse.tokenType);
81+
map.putArray("scopes", createScopeArray(authResponse.scope));
82+
83+
if (authResponse.accessTokenExpirationTime != null) {
84+
map.putString("accessTokenExpirationTime", DateUtil.formatTimestamp(authResponse.accessTokenExpirationTime));
85+
}
86+
6787
return map;
6888
}
6989
}

index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export type AuthConfiguration = BaseAuthConfiguration & {
7272
useNonce?: boolean;
7373
usePKCE?: boolean;
7474
warmAndPrefetchChrome?: boolean;
75+
skipCodeExchange?: boolean;
7576
};
7677

7778
export interface AuthorizeResult {
@@ -83,6 +84,7 @@ export interface AuthorizeResult {
8384
refreshToken: string;
8485
tokenType: string;
8586
scopes: string[];
87+
authorizationCode: string;
8688
}
8789

8890
export interface RefreshResult {

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ export const authorize = ({
155155
clientAuthMethod = 'basic',
156156
dangerouslyAllowInsecureHttpRequests = false,
157157
customHeaders,
158+
skipCodeExchange = false,
158159
}) => {
159160
validateIssuerOrServiceConfigurationEndpoints(issuer, serviceConfiguration);
160161
validateClientId(clientId);
@@ -170,6 +171,7 @@ export const authorize = ({
170171
scopes,
171172
additionalParameters,
172173
serviceConfiguration,
174+
skipCodeExchange,
173175
];
174176

175177
if (Platform.OS === 'android') {

ios/RNAppAuth.m

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ - (dispatch_queue_t)methodQueue
8888
scopes: (NSArray *) scopes
8989
additionalParameters: (NSDictionary *_Nullable) additionalParameters
9090
serviceConfiguration: (NSDictionary *_Nullable) serviceConfiguration
91+
skipCodeExchange: (BOOL) skipCodeExchange
9192
useNonce: (BOOL *) useNonce
9293
usePKCE: (BOOL *) usePKCE
9394
resolve: (RCTPromiseResolveBlock) resolve
@@ -104,6 +105,7 @@ - (dispatch_queue_t)methodQueue
104105
useNonce: useNonce
105106
usePKCE: usePKCE
106107
additionalParameters: additionalParameters
108+
skipCodeExchange: skipCodeExchange
107109
resolve: resolve
108110
reject: reject];
109111
} else {
@@ -121,6 +123,7 @@ - (dispatch_queue_t)methodQueue
121123
useNonce: useNonce
122124
usePKCE: usePKCE
123125
additionalParameters: additionalParameters
126+
skipCodeExchange: skipCodeExchange
124127
resolve: resolve
125128
reject: reject];
126129
}];
@@ -259,6 +262,7 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration
259262
useNonce: (BOOL *) useNonce
260263
usePKCE: (BOOL *) usePKCE
261264
additionalParameters: (NSDictionary *_Nullable) additionalParameters
265+
skipCodeExchange: (BOOL) skipCodeExchange
262266
resolve: (RCTPromiseResolveBlock) resolve
263267
reject: (RCTPromiseRejectBlock) reject
264268
{
@@ -296,24 +300,39 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration
296300
taskId = UIBackgroundTaskInvalid;
297301
}];
298302

299-
_currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request
303+
if (skipCodeExchange) {
304+
_currentSession = [OIDAuthorizationService presentAuthorizationRequest:request
300305
presentingViewController:appDelegate.window.rootViewController
301-
callback:^(OIDAuthState *_Nullable authState,
302-
NSError *_Nullable error) {
306+
callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error) {
303307
typeof(self) strongSelf = weakSelf;
304308
strongSelf->_currentSession = nil;
305309
[UIApplication.sharedApplication endBackgroundTask:taskId];
306310
taskId = UIBackgroundTaskInvalid;
307-
if (authState) {
308-
resolve([self formatResponse:authState.lastTokenResponse
309-
withAuthResponse:authState.lastAuthorizationResponse]);
311+
if (authorizationResponse) {
312+
resolve([self formatAuthorizationResponse:authorizationResponse]);
310313
} else {
311314
reject(@"authentication_failed", [error localizedDescription], error);
312315
}
313-
}]; // end [OIDAuthState authStateByPresentingAuthorizationRequest:request
316+
}]; // end [OIDAuthState presentAuthorizationRequest:request
317+
} else {
318+
_currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request
319+
presentingViewController:appDelegate.window.rootViewController
320+
callback:^(OIDAuthState *_Nullable authState,
321+
NSError *_Nullable error) {
322+
typeof(self) strongSelf = weakSelf;
323+
strongSelf->_currentSession = nil;
324+
[UIApplication.sharedApplication endBackgroundTask:taskId];
325+
taskId = UIBackgroundTaskInvalid;
326+
if (authState) {
327+
resolve([self formatResponse:authState.lastTokenResponse
328+
withAuthResponse:authState.lastAuthorizationResponse]);
329+
} else {
330+
reject(@"authentication_failed", [error localizedDescription], error);
331+
}
332+
}]; // end [OIDAuthState authStateByPresentingAuthorizationRequest:request
333+
}
314334
}
315335

316-
317336
/*
318337
* Refresh a token with provided OIDServiceConfiguration
319338
*/
@@ -350,6 +369,27 @@ - (void)refreshWithConfiguration: (OIDServiceConfiguration *)configuration
350369
}];
351370
}
352371

372+
373+
/*
374+
* Take raw OIDAuthorizationResponse and turn it to response format to pass to JavaScript caller
375+
*/
376+
- (NSDictionary*)formatAuthorizationResponse: (OIDAuthorizationResponse*) response {
377+
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
378+
dateFormat.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
379+
[dateFormat setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
380+
[dateFormat setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
381+
382+
return @{@"authorizationCode": response.authorizationCode ? response.authorizationCode : @"",
383+
@"state": response.state ? response.state : @"",
384+
@"accessToken": response.accessToken ? response.accessToken : @"",
385+
@"accessTokenExpirationDate": response.accessTokenExpirationDate ? [dateFormat stringFromDate:response.accessTokenExpirationDate] : @"",
386+
@"tokenType": response.tokenType ? response.tokenType : @"",
387+
@"idToken": response.idToken ? response.idToken : @"",
388+
@"scopes": response.scope ? [response.scope componentsSeparatedByString:@" "] : [NSArray new],
389+
@"additionalParameters": response.additionalParameters,
390+
};
391+
}
392+
353393
/*
354394
* Take raw OIDTokenResponse and turn it to a token response format to pass to JavaScript caller
355395
*/

0 commit comments

Comments
 (0)