Skip to content

Commit

Permalink
Merge pull request #1 from visible-xyz/fix-sf-safari-view-controller-…
Browse files Browse the repository at this point in the history
…logout
  • Loading branch information
tigran-visible authored Feb 13, 2024
2 parents c677a13 + 1b287e1 commit 7bd788a
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 22 deletions.
1 change: 0 additions & 1 deletion .github/CODEOWNERS

This file was deleted.

2 changes: 1 addition & 1 deletion android/src/main/java/com/auth0/react/A0Auth0Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public void onFailure(@NonNull AuthenticationException error) {
}

@ReactMethod
public void webAuthLogout(String scheme, boolean federated, String redirectUri, Promise promise) {
public void webAuthLogout(String scheme, boolean federated, String redirectUri, int safariViewControllerPresentationStyle, Promise promise) {
WebAuthProvider.LogoutBuilder builder = WebAuthProvider.logout(this.auth0)
.withScheme(scheme);
if(federated) {
Expand Down
4 changes: 2 additions & 2 deletions ios/A0Auth0.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ - (dispatch_queue_t)methodQueue
[self.nativeBridge webAuthWithState:state redirectUri:redirectUri nonce:nonce audience:audience scope:scope connection:connection maxAge:maxAge organization:organization invitationUrl:invitationUrl leeway:leeway ephemeralSession:ephemeralSession safariViewControllerPresentationStyle:safariViewControllerPresentationStyle additionalParameters:additionalParameters resolve:resolve reject:reject];
}

RCT_EXPORT_METHOD(webAuthLogout:(NSString *)scheme federated:(BOOL)federated redirectUri:(NSString *)redirectUri resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[self.nativeBridge webAuthLogoutWithFederated:federated redirectUri:redirectUri resolve:resolve reject:reject];
RCT_EXPORT_METHOD(webAuthLogout:(NSString *)scheme federated:(BOOL)federated redirectUri:(NSString *)redirectUri safariViewControllerPresentationStyle:(NSInteger)safariViewControllerPresentationStyle resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[self.nativeBridge webAuthLogoutWithFederated:federated redirectUri:redirectUri safariViewControllerPresentationStyle: safariViewControllerPresentationStyle resolve:resolve reject:reject];
}

RCT_EXPORT_METHOD(resumeWebAuth:(NSString *)url resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
Expand Down
6 changes: 5 additions & 1 deletion ios/NativeBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,15 @@ public class NativeBridge: NSObject {

}

@objc public func webAuthLogout(federated: Bool, redirectUri: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
@objc public func webAuthLogout(federated: Bool, redirectUri: String, safariViewControllerPresentationStyle: Int, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
let builder = Auth0.webAuth(clientId: self.clientId, domain: self.domain)
if let value = URL(string: redirectUri) {
let _ = builder.redirectURL(value)
}
if let presentationStyle = UIModalPresentationStyle(rawValue: safariViewControllerPresentationStyle), safariViewControllerPresentationStyle != 99 {
let _ = builder.provider(WebAuthentication.safariProvider(style: presentationStyle))
}

builder.clearSession(federated: federated) { result in
switch result {
case .success:
Expand Down
4 changes: 3 additions & 1 deletion src/internal-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ export type Auth0Module = {
webAuthLogout: (
scheme: string,
federated: boolean,
redirectUri: string
redirectUri: string,
safariViewControllerPresentationStyle: number
) => Promise<void>;
resumeWebAuth: (url: string) => Promise<void>;
saveCredentials: (credentials: Credentials) => Promise<void>;
Expand Down Expand Up @@ -122,6 +123,7 @@ export type AgentLogoutOptions = {
federated?: boolean;
returnToUrl?: string;
useLegacyCallbackUrl?: boolean;
safariViewControllerPresentationStyle?: number;
};

export interface AgentLoginOptions {
Expand Down
11 changes: 11 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,17 @@ export interface ClearSessionOptions {
* This will use older callback URL. See {@link https://github.com/auth0/react-native-auth0/blob/master/MIGRATION_GUIDE.md#callback-url-migration} for more details.
*/
useLegacyCallbackUrl?: boolean;

/**
* **iOS only:** Uses `SFSafariViewController` instead of `ASWebAuthenticationSession`. If empty object is set, the presentationStyle defaults to {@link SafariViewControllerPresentationStyle.fullScreen}
*
* This can be used as a boolean value or as an object which sets the `presentationStyle`.
*/
useSFSafariViewController?:
| {
presentationStyle?: SafariViewControllerPresentationStyle;
}
| boolean;
}

export interface GetUserOptions {
Expand Down
33 changes: 31 additions & 2 deletions src/webauth/__tests__/agent.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ describe('Agent', () => {
expect(mockLogin).toBeCalledWith(
'test',
true,
'test://test.com/ios/com.my.app/callback'
'test://test.com/ios/com.my.app/callback',
99
);
});

Expand All @@ -227,7 +228,35 @@ describe('Agent', () => {
expect(mockLogin).toBeCalledWith(
'com.my.app.auth0',
false,
'redirect://redirect.com'
'redirect://redirect.com',
99
);
});
it('should be called with correct arguments when SFSafariViewController is set', async () => {
let domain = 'test.com';
let clientId = 'client id value';
const mock = jest
.spyOn(nativeUtils, '_ensureNativeModuleIsInitialized')
.mockImplementation(() => Promise.resolve(true));
const mockLogout = jest
.spyOn(NativeModules.A0Auth0, 'webAuthLogout')
.mockImplementation(() => Promise.resolve(true));
await agent.logout(
{
clientId: clientId,
domain: domain,
},
{
returnToUrl: 'redirect://redirect.com',
safariViewControllerPresentationStyle: 0,
}
);
expect(mock).toBeCalledWith(NativeModules.A0Auth0, clientId, domain);
expect(mockLogout).toBeCalledWith(
'com.my.app.auth0',
false,
'redirect://redirect.com',
0
);
});
});
Expand Down
40 changes: 39 additions & 1 deletion src/webauth/__tests__/webauth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,45 @@ describe('WebAuth', () => {
await webauth.clearSession(parameters, options);
expect(showMock).toHaveBeenCalledWith(
{ clientId, domain },
{ ...parameters, ...options }
{ ...parameters, ...options, useSFSafariViewController: undefined }
);
showMock.mockRestore();
});
it('should call clearSession for iOS with a presentation style set to true for SFSafariViewController', async () => {
let parameters = {
federated: true,
returnToUrl: 'https://redirect.redirect.com',
};
let options = {
customScheme: 'scheme',
useSFSafariViewController: true,
};
const showMock = jest
.spyOn(webauth.agent, 'logout')
.mockImplementation(() => Promise.resolve());
await webauth.clearSession(parameters, options);
expect(showMock).toHaveBeenCalledWith(
{ clientId, domain },
{ ...parameters, ...options, safariViewControllerPresentationStyle: 0 }
);
showMock.mockRestore();
});
it('should call clearSession for iOS with a presentation style set to a provided presentation style value for SFSafariViewController', async () => {
let parameters = {
federated: true,
returnToUrl: 'https://redirect.redirect.com',
};
let options = {
customScheme: 'scheme',
useSFSafariViewController: { presentationStyle: 4 },
};
const showMock = jest
.spyOn(webauth.agent, 'logout')
.mockImplementation(() => Promise.resolve());
await webauth.clearSession(parameters, options);
expect(showMock).toHaveBeenCalledWith(
{ clientId, domain },
{ ...parameters, ...options, safariViewControllerPresentationStyle: 4 }
);
showMock.mockRestore();
});
Expand Down
55 changes: 42 additions & 13 deletions src/webauth/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,26 +82,55 @@ class Agent {
parameters: AgentParameters,
options: AgentLogoutOptions
): Promise<void> {
let linkSubscription: EmitterSubscription | null = null;

if (!NativeModules.A0Auth0) {
return Promise.reject(
new Error(
'Missing NativeModule. React Native versions 0.60 and up perform auto-linking. Please see https://github.com/react-native-community/cli/blob/master/docs/autolinking.md.'
)
);
}
let federated = options.federated ?? false;
let scheme = this.getScheme(
options.useLegacyCallbackUrl ?? false,
options.customScheme
);
let redirectUri =
options.returnToUrl ?? this.callbackUri(parameters.domain, scheme);
await _ensureNativeModuleIsInitialized(
NativeModules.A0Auth0,
parameters.clientId,
parameters.domain
);
return A0Auth0.webAuthLogout(scheme, federated, redirectUri);
return new Promise(async (resolve, reject) => {
let federated = options.federated ?? false;
let scheme = this.getScheme(
options.useLegacyCallbackUrl ?? false,
options.customScheme
);
let redirectUri =
options.returnToUrl ?? this.callbackUri(parameters.domain, scheme);

if (
Platform.OS === 'ios' &&
options.safariViewControllerPresentationStyle !== undefined
) {
linkSubscription = Linking.addEventListener('url', async (event) => {
try {
linkSubscription?.remove();
await A0Auth0.resumeWebAuth(redirectUri);
} catch (error) {
reject(error);
}
});
}
await _ensureNativeModuleIsInitialized(
NativeModules.A0Auth0,
parameters.clientId,
parameters.domain
);
try {
await A0Auth0.webAuthLogout(
scheme,
federated,
redirectUri,
options.safariViewControllerPresentationStyle ?? 99
);
resolve();
} catch (error) {
linkSubscription?.remove();
reject(error);
}
});
}

private getScheme(
Expand Down
12 changes: 12 additions & 0 deletions src/webauth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,22 @@ class WebAuth {
options: ClearSessionOptions = {}
) {
const { agent, domain, clientId } = this;

let presentationStyle;
if (typeof options.useSFSafariViewController === 'object') {
presentationStyle =
options.useSFSafariViewController?.presentationStyle ??
SafariViewControllerPresentationStyle.fullScreen;
} else if (options.useSFSafariViewController === true) {
presentationStyle = SafariViewControllerPresentationStyle.fullScreen;
} else {
presentationStyle = undefined;
}
return agent.logout(
{ clientId, domain },
{
...parameters,
safariViewControllerPresentationStyle: presentationStyle,
...options,
}
);
Expand Down

0 comments on commit 7bd788a

Please sign in to comment.