From 96afce50561f5cf2388869d153dd196e6936d379 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 23 Nov 2022 20:05:53 +0000 Subject: [PATCH] Add support for prefersEphemeralSession on iOS. Refs #771 (#787) --- Example/App.js | 1 + Example/ios/Podfile.lock | 4 +-- README.md | 1 + index.d.ts | 11 ++++++++- index.js | 4 +++ index.spec.js | 28 ++++++++++++++------- ios/RNAppAuth.m | 46 +++++++++++++++++++++++++++++------ react-native-app-auth.podspec | 2 +- 8 files changed, 77 insertions(+), 20 deletions(-) diff --git a/Example/App.js b/Example/App.js index 1843c2ae..97ed16cf 100644 --- a/Example/App.js +++ b/Example/App.js @@ -71,6 +71,7 @@ const App = () => { const newAuthState = await authorize({ ...config, connectionTimeoutSeconds: 5, + iosPrefersEphemeralSession: true }); setAuthState({ diff --git a/Example/ios/Podfile.lock b/Example/ios/Podfile.lock index 946fff0e..f4bb2dea 100644 --- a/Example/ios/Podfile.lock +++ b/Example/ios/Podfile.lock @@ -192,7 +192,7 @@ PODS: - React-jsi (= 0.63.2) - React-jsinspector (0.63.2) - react-native-app-auth (6.4.3): - - AppAuth (~> 1.4) + - AppAuth (~> 1.6) - React-Core - React-RCTActionSheet (0.63.2): - React-Core/RCTActionSheetHeaders (= 0.63.2) @@ -366,7 +366,7 @@ SPEC CHECKSUMS: React-jsi: 54245e1d5f4b690dec614a73a3795964eeef13a8 React-jsiexecutor: 8ca588cc921e70590820ce72b8789b02c67cce38 React-jsinspector: b14e62ebe7a66e9231e9581279909f2fc3db6606 - react-native-app-auth: e5b48009fda193a6a6808ec8f3d2cf159d9cbba4 + react-native-app-auth: 0aaa426c98f354afa487f1d792bd711d83495a22 React-RCTActionSheet: 910163b6b09685a35c4ebbc52b66d1bfbbe39fc5 React-RCTAnimation: 9a883bbe1e9d2e158d4fb53765ed64c8dc2200c6 React-RCTBlob: 39cf0ece1927996c4466510e25d2105f67010e13 diff --git a/README.md b/README.md index 6fb9867b..4d1c8cdc 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ with optional overrides. - **usePKCE** - (`boolean`) (default: true) optionally allows not sending the code_challenge parameter and skipping PKCE code verification, to support non-compliant providers. - **skipCodeExchange** - (`boolean`) (default: false) just return the authorization response, instead of automatically exchanging the authorization code. This is useful if this exchange needs to be done manually (not client-side) - **iosCustomBrowser** - (`string`) (default: undefined) _IOS_ override the used browser for authorization, used to open an external browser. If no value is provided, the `SFAuthenticationSession` or `SFSafariViewController` are used. +- **iosPrefersEphemeralSession** - (`boolean`) (default: `false`) _IOS_ indicates whether the session should ask the browser for a private authentication session. - **androidAllowCustomBrowsers** - (`string[]`) (default: undefined) _ANDROID_ override the used browser for authorization. If no value is provided, all browsers are allowed. - **connectionTimeoutSeconds** - (`number`) configure the request timeout interval in seconds. This must be a positive number. The default values are 60 seconds on iOS and 15 seconds on Android. diff --git a/index.d.ts b/index.d.ts index a380b8a8..aaa5c5ee 100644 --- a/index.d.ts +++ b/index.d.ts @@ -80,12 +80,21 @@ export type AuthConfiguration = BaseAuthConfiguration & { warmAndPrefetchChrome?: boolean; skipCodeExchange?: boolean; iosCustomBrowser?: 'safari' | 'chrome' | 'opera' | 'firefox'; - androidAllowCustomBrowsers?: ('chrome' | 'chromeCustomTab' | 'firefox' | 'firefoxCustomTab' | 'samsung' | 'samsungCustomTab')[] + androidAllowCustomBrowsers?: ( + | 'chrome' + | 'chromeCustomTab' + | 'firefox' + | 'firefoxCustomTab' + | 'samsung' + | 'samsungCustomTab' + )[]; + iosPrefersEphemeralSession?: boolean; }; export type EndSessionConfiguration = BaseAuthConfiguration & { additionalParameters?: { [name: string]: string }; dangerouslyAllowInsecureHttpRequests?: boolean; + iosPrefersEphemeralSession?: boolean; }; export interface AuthorizeResult { diff --git a/index.js b/index.js index 019cdd23..afe37552 100644 --- a/index.js +++ b/index.js @@ -210,6 +210,7 @@ export const authorize = ({ iosCustomBrowser = null, androidAllowCustomBrowsers = null, connectionTimeoutSeconds, + iosPrefersEphemeralSession = false, }) => { validateIssuerOrServiceConfigurationEndpoints(issuer, serviceConfiguration); validateClientId(clientId); @@ -245,6 +246,7 @@ export const authorize = ({ nativeMethodArguments.push(useNonce); nativeMethodArguments.push(usePKCE); nativeMethodArguments.push(iosCustomBrowser); + nativeMethodArguments.push(iosPrefersEphemeralSession); } return RNAppAuth.authorize(...nativeMethodArguments); @@ -356,6 +358,7 @@ export const logout = ( additionalParameters, dangerouslyAllowInsecureHttpRequests = false, iosCustomBrowser = null, + iosPrefersEphemeralSession = false, androidAllowCustomBrowsers = null, }, { idToken, postLogoutRedirectUrl } @@ -379,6 +382,7 @@ export const logout = ( if (Platform.OS === 'ios') { nativeMethodArguments.push(iosCustomBrowser); + nativeMethodArguments.push(iosPrefersEphemeralSession); } return RNAppAuth.logout(...nativeMethodArguments); diff --git a/index.spec.js b/index.spec.js index 29b53fd4..43973926 100644 --- a/index.spec.js +++ b/index.spec.js @@ -61,6 +61,7 @@ describe('AppAuth', () => { connectionTimeoutSeconds: TIMEOUT_SEC, skipCodeExchange: false, iosCustomBrowser: 'safari', + iosPrefersEphemeralSession: true, androidAllowCustomBrowsers: ['chrome'], }; @@ -533,7 +534,8 @@ describe('AppAuth', () => { config.additionalHeaders, config.useNonce, config.usePKCE, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); @@ -562,7 +564,8 @@ describe('AppAuth', () => { null, true, true, - null + null, + false ); }); @@ -587,7 +590,8 @@ describe('AppAuth', () => { config.additionalHeaders, config.useNonce, config.usePKCE, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); }); @@ -609,7 +613,8 @@ describe('AppAuth', () => { additionalHeaders, config.useNonce, config.usePKCE, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); @@ -641,7 +646,8 @@ describe('AppAuth', () => { config.additionalHeaders, true, true, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); @@ -660,7 +666,8 @@ describe('AppAuth', () => { config.additionalHeaders, false, true, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); }); @@ -681,7 +688,8 @@ describe('AppAuth', () => { config.additionalHeaders, config.useNonce, true, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); @@ -700,7 +708,8 @@ describe('AppAuth', () => { config.additionalHeaders, config.useNonce, false, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); }); @@ -1120,7 +1129,8 @@ describe('AppAuth', () => { '_redirect_', config.serviceConfiguration, config.additionalParameters, - config.iosCustomBrowser + config.iosCustomBrowser, + config.iosPrefersEphemeralSession ); }); }); diff --git a/ios/RNAppAuth.m b/ios/RNAppAuth.m index 7f5b7f9c..5e485314 100644 --- a/ios/RNAppAuth.m +++ b/ios/RNAppAuth.m @@ -34,6 +34,8 @@ - (dispatch_queue_t)methodQueue */ static NSUInteger const kCodeVerifierBytes = 32; + + RCT_EXPORT_MODULE() RCT_REMAP_METHOD(register, @@ -98,6 +100,7 @@ - (dispatch_queue_t)methodQueue useNonce: (BOOL *) useNonce usePKCE: (BOOL *) usePKCE iosCustomBrowser: (NSString *) iosCustomBrowser + prefersEphemeralSession: (BOOL *) prefersEphemeralSession resolve: (RCTPromiseResolveBlock) resolve reject: (RCTPromiseRejectBlock) reject) { @@ -116,6 +119,7 @@ - (dispatch_queue_t)methodQueue additionalParameters: additionalParameters skipCodeExchange: skipCodeExchange iosCustomBrowser: iosCustomBrowser + prefersEphemeralSession: prefersEphemeralSession resolve: resolve reject: reject]; } else { @@ -136,6 +140,7 @@ - (dispatch_queue_t)methodQueue additionalParameters: additionalParameters skipCodeExchange: skipCodeExchange iosCustomBrowser: iosCustomBrowser + prefersEphemeralSession: prefersEphemeralSession resolve: resolve reject: reject]; }]; @@ -199,6 +204,7 @@ - (dispatch_queue_t)methodQueue serviceConfiguration: (NSDictionary *_Nullable) serviceConfiguration additionalParameters: (NSDictionary *_Nullable) additionalParameters iosCustomBrowser: (NSString *) iosCustomBrowser + prefersEphemeralSession: (BOOL *) prefersEphemeralSession resolve:(RCTPromiseResolveBlock) resolve reject: (RCTPromiseRejectBlock) reject) { @@ -209,6 +215,7 @@ - (dispatch_queue_t)methodQueue postLogoutRedirectURL: postLogoutRedirectURL additionalParameters: additionalParameters iosCustomBrowser: iosCustomBrowser + prefersEphemeralSession: prefersEphemeralSession resolve: resolve reject: reject]; @@ -224,6 +231,7 @@ - (dispatch_queue_t)methodQueue postLogoutRedirectURL: postLogoutRedirectURL additionalParameters: additionalParameters iosCustomBrowser: iosCustomBrowser + prefersEphemeralSession: prefersEphemeralSession resolve: resolve reject: reject]; }]; @@ -322,6 +330,7 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration additionalParameters: (NSDictionary *_Nullable) additionalParameters skipCodeExchange: (BOOL) skipCodeExchange iosCustomBrowser: (NSString *) iosCustomBrowser + prefersEphemeralSession: (BOOL *) prefersEphemeralSession resolve: (RCTPromiseResolveBlock) resolve reject: (RCTPromiseRejectBlock) reject { @@ -383,9 +392,16 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration externalUserAgent:externalUserAgent callback:callback]; } else { - _currentSession = [OIDAuthorizationService presentAuthorizationRequest:request + if (@available(iOS 13, *)) { + _currentSession = [OIDAuthorizationService presentAuthorizationRequest:request + presentingViewController:presentingViewController + prefersEphemeralSession:prefersEphemeralSession + callback:callback]; + } else { + _currentSession = [OIDAuthorizationService presentAuthorizationRequest:request presentingViewController:presentingViewController callback:callback]; + } } } else { @@ -408,10 +424,16 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration [self getErrorMessage: error], error); } }; - - _currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request - presentingViewController:presentingViewController - callback:callback]; + if (@available(iOS 13, *)) { + _currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request + presentingViewController:presentingViewController + prefersEphemeralSession:prefersEphemeralSession + callback:callback]; + } else { + _currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request + presentingViewController:presentingViewController + callback:callback]; + } } } } @@ -458,6 +480,7 @@ - (void)endSessionWithConfiguration: (OIDServiceConfiguration *) configuration postLogoutRedirectURL: (NSString *) postLogoutRedirectURL additionalParameters: (NSDictionary *_Nullable) additionalParameters iosCustomBrowser: (NSString *) iosCustomBrowser + prefersEphemeralSession: (BOOL *) prefersEphemeralSession resolve: (RCTPromiseResolveBlock) resolve reject: (RCTPromiseRejectBlock) reject { @@ -482,7 +505,8 @@ - (void)endSessionWithConfiguration: (OIDServiceConfiguration *) configuration UIViewController *presentingViewController = appDelegate.window.rootViewController.view.window ? appDelegate.window.rootViewController : appDelegate.window.rootViewController.presentedViewController; - id externalUserAgent = iosCustomBrowser != nil ? [self getCustomBrowser: iosCustomBrowser] : [self getExternalUserAgentWithPresentingViewController:presentingViewController]; + id externalUserAgent = iosCustomBrowser != nil ? [self getCustomBrowser: iosCustomBrowser] : [self getExternalUserAgentWithPresentingViewController:presentingViewController + prefersEphemeralSession:prefersEphemeralSession]; _currentSession = [OIDAuthorizationService presentEndSessionRequest: endSessionRequest externalUserAgent: externalUserAgent @@ -695,12 +719,20 @@ - (NSString*)getErrorMessage: (NSError*) error { } - (id)getExternalUserAgentWithPresentingViewController: (UIViewController *)presentingViewController + prefersEphemeralSession: (BOOL *) prefersEphemeralSession { id externalUserAgent; #if TARGET_OS_MACCATALYST externalUserAgent = [[OIDExternalUserAgentCatalyst alloc] initWithPresentingViewController:presentingViewController]; #elif TARGET_OS_IOS - externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:presentingViewController]; + if (@available(iOS 13, *)) { + externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController: + presentingViewController + prefersEphemeralSession:prefersEphemeralSession]; + } else { + externalUserAgent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController: + presentingViewController]; + } #elif TARGET_OS_OSX externalUserAgent = [[OIDExternalUserAgentMac alloc] init]; #endif diff --git a/react-native-app-auth.podspec b/react-native-app-auth.podspec index 2dcafa6f..dee88ea6 100644 --- a/react-native-app-auth.podspec +++ b/react-native-app-auth.podspec @@ -14,5 +14,5 @@ Pod::Spec.new do |s| s.source_files = 'ios/**/*.{h,m}' s.requires_arc = true s.dependency 'React-Core' - s.dependency 'AppAuth', '~> 1.4' + s.dependency 'AppAuth', '~> 1.6' end