openid / AppAuth-iOS

iOS and macOS SDK for communicating with OAuth 2.0 and OpenID Connect providers.
https://openid.github.io/AppAuth-iOS
Apache License 2.0
1.77k stars 774 forks source link

iOS Obj-C presentEndSessionRequest callback not getting called #698

Closed mjswan closed 2 years ago

mjswan commented 2 years ago

Describe the bug When using iOS Objective-C presentEndSessionRequest, one of the parameters is 'callback'. However, even though the call seems to succeed (the OAuth credentials are reset) and the annoying 'Sign In' alert appears (#643), the callback doesn't get called.

Expected behavior I expect the callback to be called. ;-)

Smartphone

Here is the code I'm using in case it helps, where logoutRedirectURL is obfuscated. I've tried with and without the additionalParameters.

#define logoutRedirectURL @"com.mydomain.mobile:/logout"

        NSString *issuerURL = [[NSString stringWithString:currentAccount.serverName] lowercaseString];
        NSURL *url = [NSURL URLWithString:issuerURL];
        issuerURL = [NSString stringWithFormat:@"%@/auth/realms/%@", issuerURL, url.host];
        NSURL *issuer = [NSURL URLWithString:issuerURL];

        [OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer
            completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) {
            if (configuration) {
                // build end session request
                NSURL *redirectURL = [NSURL URLWithString:logoutRedirectURL];
                NSDictionary *additionalParameters = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:logoutRedirectURL, currentAccount.idToken, nil]
                    forKeys:[NSArray arrayWithObjects:@"post_logout_redirect_uri", @"id_token_hint", nil]];

                OIDEndSessionRequest *request = [[OIDEndSessionRequest alloc] initWithConfiguration:configuration idTokenHint:currentAccount.idToken postLogoutRedirectURL:redirectURL
                    additionalParameters:additionalParameters];
                OIDExternalUserAgentIOS *agent = [[OIDExternalUserAgentIOS alloc] initWithPresentingViewController:self];
                [OIDAuthorizationService presentEndSessionRequest:request externalUserAgent:agent callback:^(OIDEndSessionResponse * _Nullable endSessionResponse, NSError * _Nullable error) {
                    // note: this callback never seems to happen...
                    if (endSessionResponse) {
                    } else {
                        VLog(@"endSession error: %@", [error localizedDescription]);
                    }
                }];
            }
        }];
petea commented 2 years ago

Can you verify that your app is receiving the custom URL scheme redirect via the application:openURL:options: delegate and that resumeExternalUserAgentFlowWithURL: is being called?

mjswan commented 2 years ago

No, application:openURL:options: is not being called at any time.

BTW, when calling authStateByPresentingAuthorizationRequest, its callback is called and the token information is correct.

petea commented 2 years ago

Check that your postLogoutRedirectURL is correct and that your app is registered to respond it its custom URL scheme.

Not sure why you would need the additionalParameters.

asutah commented 2 years ago

Same problem I am facing in my project recently. But earlier it was working. Facing issue only in safari browser. At the time login call back function not calling and taking maximum time loading in browser.
Please make this issue as a high priority.

arindam94 commented 2 years ago

Facing same problem in safari browser ios -15 os. Not calling call back function. pls help asap

Sandy417 commented 2 years ago

We are facing a similar problem. from the mobile app, Safari browser is loaded with SSO URL and the control is not coming back to the native app and keeps on showing loading indicator.

petea commented 2 years ago

@asutah @arindam94 @Sandy417 are you using presentEndSessionRequest as well? Can you tell me which IDP you're working with?

mjswan commented 2 years ago

Can you verify that your app is receiving the custom URL scheme redirect via the application:openURL:options: delegate and that resumeExternalUserAgentFlowWithURL: is being called?

I did find one problem: the redirect URL I was using was an unusual format (it had periods in it). Once I took the periods out of it and used 'xcrun' from the Mac OS CLI (e.g. xcrun simctl openurl booted myredirecturl), then application:openURL:options: got called.

However, neither 'application:openURL:options:' nor the presentEndSessionRequest callback is getting called when executing presentEndSessionRequest, which is my original problem.

arindam94 commented 2 years ago

@petea We are using Appauth for SSO and we used custom browser (mobile iron Web@work) its working but in safari we are facing these issue. whereas its not callback to the actual application. Which used to work before for years like these. please check on safari browser. I

Sandy417 commented 2 years ago

Can you verify that your app is receiving the custom URL scheme redirect via the application:openURL:options: delegate and that resumeExternalUserAgentFlowWithURL: is being called? ---- No, we are not receiving this callback

And this issue is happening in safari

@petea @mjswan

Sandy417 commented 2 years ago

Can you please let us now the possible reasons for not receiving a callback in safari.

We tried in a different browser and it is working fine.

@petea

Mkalla commented 2 years ago

Facing the same issues:

nnordling commented 2 years ago

We faced a similar issue where we couldn't log out despite using correct redirect uri, endpoint etc. What solved it for us was assigning presentEndSessionRequest to a variable. I haven't figured why it works, or if it's the correct way, but it somehow works. ¯_(ツ)_/¯

Example (in Swift) private let userAgentSession: OIDExternalUserAgentSession? init() { userAgentSession = nil } self.userAgentSession = OIDAuthorizationService.present(endSessionsRequest,...)

mjswan commented 2 years ago

@nnordling -- Thank you for your reply. I can confirm that assigning the return from presentEndSessionRequest to a variable does indeed cause the callback to be called when using Safari.

tiwari1amrit commented 1 year ago

Did anyone solve this logout issue?

mjswan commented 1 year ago

@tiwariammit -- please have a look at the two preceding comments: assigning the return to a variable does cause the callback to be called.

tiwari1amrit commented 1 year ago

@mjswan thank you for your response. I successfully able to logout, but the issue is that after closing signout dialogue can'ta able to track the completion of below function. OIDAuthorizationService.present(endSessionRequest, externalUserAgent: agent!) {response, error in The flow won't go inside block. So, can't able to track whether user logout successfully or not.

Here is my full code

` let authEndpoint = URL(string: Urls.Hydra.authEndpoint())! let tokenEndpoint = URL(string: Urls.Hydra.tokenEndpoint())! let redirectURL = URL(string: Urls.Hydra.redirectCallbackURL())! let logoutEndpointString = Urls.Hydra.logout() + "/?redirect_uri=" + Urls.Hydra.redirectCallbackURL() let logoutEndpoint = URL(string: logoutEndpointString)! let configuration = OIDServiceConfiguration(authorizationEndpoint: authEndpoint, tokenEndpoint: tokenEndpoint, issuer: nil, registrationEndpoint: nil, endSessionEndpoint: logoutEndpoint)

    guard let idToken = hydraAuthStateModel.idToken else {
        return
    }

    let endSessionRequest = OIDEndSessionRequest(configuration: configuration,
                                                 idTokenHint: idToken,
                                                 postLogoutRedirectURL: redirectURL,
                                                 state: hydraAuthStateModel.state!,
                                                 additionalParameters: nil)

    let agent = OIDExternalUserAgentIOS(presenting: viewController)

    OIDAuthorizationService.present(endSessionRequest,
                                    externalUserAgent: agent!) {response, error in

        if let error = error {
            print("Authorization error: \(error.localizedDescription)")
            return
        }

        guard let response = response else {
            print("Authorization response is nil.")
            return
        }

        print("Authorization response: \(response)")

        success?()
    }

`

mjswan commented 1 year ago

@tiwariammit You're still not assigning the return from OIDAuthorizationService.present(... to a variable, as explained in the comments above. (I'm not a Swift person but I think the syntax is correct.)

Try changing this: OIDAuthorizationService.present(endSessionRequest

to this: let presentReturn = OIDAuthorizationService.present(endSessionRequest

tiwari1amrit commented 1 year ago

@mjswan If I assign let presentReturn = OIDAuthorizationService.present(endSessionRequest then how can I catch presentReturn because there are not such function. Please have a look on this class.


/*! @brief Represents an in-flight external user-agent session.
 */
@protocol OIDExternalUserAgentSession <NSObject>

/*! @brief Cancels the code flow session, invoking the request's callback with a cancelled error.
    @remarks Has no effect if called more than once, or after a
        @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message was received.
        Will cause an error with code: @c ::OIDErrorCodeProgramCanceledAuthorizationFlow to be
        passed to the @c callback block passed to
        @c OIDAuthorizationService.presentAuthorizationRequest:presentingViewController:callback:
 */
- (void)cancel;

/*! @brief Cancels the code flow session, invoking the request's callback with a cancelled error.
    @remarks Has no effect if called more than once, or after a
        @c OIDExternalUserAgentSession.resumeExternalUserAgentFlowWithURL: message was received.
        Will cause an error with code: @c ::OIDErrorCodeProgramCanceledAuthorizationFlow to be
        passed to the @c callback block passed to
        @c OIDAuthorizationService.presentAuthorizationRequest:presentingViewController:callback:
    @param completion The block to be called when the cancel operation ends
 */
- (void)cancelWithCompletion:(nullable void (^)(void))completion;

/*! @brief Clients should call this method with the result of the external user-agent code flow if
        it becomes available.
    @param URL The redirect URL invoked by the server.
    @discussion When the URL represented a valid response, implementations should clean up any
        left-over UI state from the request, for example by closing the
        \SFSafariViewController or loopback HTTP listener if those were used. The completion block
        of the pending request should then be invoked.
    @remarks Has no effect if called more than once, or after a @c cancel message was received.
    @return YES if the passed URL matches the expected redirect URL and was consumed, NO otherwise.
 */
- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)URL;

/*! @brief @c OIDExternalUserAgent or clients should call this method when the
        external user-agent flow failed with a non-OAuth error.
    @param error The error that is the reason for the failure of this external flow.
    @remarks Has no effect if called more than once, or after a @c cancel message was received.
 */
- (void)failExternalUserAgentFlowWithError:(NSError *)error;

@end

NS_ASSUME_NONNULL_END
nnordling commented 1 year ago

Do you have an OIDExternalUserAgentSession variable declared in the AppDelegate? If so you assign OIDAuthorizationService.present(endSessionRequest..) to that and it should work. For example;

(in AppDelegate) var currentAuthorizationFlow: OIDExternalUserAgentSession?
(other class) appDelegate.currentAuthorizationFlow = OIDAuthorizationService.present(endSessionRequest..)
tiwari1amrit commented 1 year ago

@nnordling I don't have variable any OIDExternalUserAgentSession variable declared on AppDelegate.