AppAuth-iOS icon indicating copy to clipboard operation
AppAuth-iOS copied to clipboard

"Nonce mismatch"

Open Maxatma opened this issue 3 years ago • 2 comments

Hello, gentlemen, I'm using auto-exchange example code to have my user be authentificate:

let request = OIDAuthorizationRequest(
                configuration: my.configuration,
                clientId: my.clientID,
                clientSecret: nil,
                scopes: [OIDScopeOpenID],
                redirectURL: my.redirect,
                responseType: OIDResponseTypeCode,
                additionalParameters: nil)
            
                
            print("nonce is ", request.nonce)
            self.currentAuthorizationFlow = OIDAuthState.authState(
                byPresenting: request,
                presenting: self.vc) { authState, error in
                    
                    
                    if let error = error {
                        completion(.failure(error))
                        return
                    }
                    
                    if let authState = authState {
                        //saving authState
                        completion(.success(()))
                    } else {
                        completion(.failure(Error.noState))
                    }
                }

But in the result, I have a "Nonce mismatch" error.

After debugging I found that "nonce"s are the same in 1 first step (getting authorizationResponse)

in OIDAuthState (IOS)

+ (id<OIDExternalUserAgentSession>)
    authStateByPresentingAuthorizationRequest:(OIDAuthorizationRequest *)authorizationRequest
                            externalUserAgent:(id<OIDExternalUserAgent>)externalUserAgent
                                     callback:(OIDAuthStateAuthorizationCallback)callback {
  // presents the authorization request
  id<OIDExternalUserAgentSession> authFlowSession = [OIDAuthorizationService
      presentAuthorizationRequest:authorizationRequest
                externalUserAgent:externalUserAgent
                         callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse,
                                    NSError *_Nullable authorizationError) {
                           // inspects response and processes further if needed (e.g. authorization
                           // code exchange)
                           if (authorizationResponse) {
                             if ([authorizationRequest.responseType
                                     isEqualToString:OIDResponseTypeCode]) {
                               // if the request is for the code flow (NB. not hybrid), assumes the
                               // code is intended for this client, and performs the authorization
                               // code exchange
                               OIDTokenRequest *tokenExchangeRequest =
                                   [authorizationResponse tokenExchangeRequest];
                               [OIDAuthorizationService performTokenRequest:tokenExchangeRequest
                                              originalAuthorizationResponse:authorizationResponse
                                   callback:^(OIDTokenResponse *_Nullable tokenResponse,
                                                         NSError *_Nullable tokenError) {
                                                OIDAuthState *authState;
                                                if (tokenResponse) {
                                                  authState = [[OIDAuthState alloc]
                                                      initWithAuthorizationResponse:
                                                          authorizationResponse
                                                                      tokenResponse:tokenResponse];
                                                }
                                                callback(authState, tokenError);
                               }];
                             } else {
                               // hybrid flow (code id_token). Two possible cases:
                               // 1. The code is not for this client, ie. will be sent to a
                               //    webservice that performs the id token verification and token
                               //    exchange
                               // 2. The code is for this client and, for security reasons, the
                               //    application developer must verify the id_token signature and
                               //    c_hash before calling the token endpoint
                               OIDAuthState *authState = [[OIDAuthState alloc]
                                   initWithAuthorizationResponse:authorizationResponse];
                               callback(authState, authorizationError);
                             }
                           } else {
                             callback(nil, authorizationError);
                           }
                         }];
  return authFlowSession;
}

But after the second step (tokenExchangeRequest), they aren't the same. I mean, not quite the same.

in a OIDAuthorizationService:

+ (void)performTokenRequest:(OIDTokenRequest *)request
    originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse
                         callback:(OIDTokenCallback)callback {
*****
  NSURLSession *session = [OIDURLSessionProvider session];
  [[session dataTaskWithRequest:URLRequest
              completionHandler:^(NSData *_Nullable data,
                                  NSURLResponse *_Nullable response,
                                  NSError *_Nullable error) {

data will have a different nonce, shorter. They look something like that:

(lldb) po idToken.nonce
YonOCCL3cUNwt1KqgstOW5bWcPh9F0dJ

(lldb) po authorizationResponse.request.nonce
YonOCCL3cUNwt1KqgstOW5bWcPh9F0dJec-IFIJZzTY

it is happening every time, returned nonce is just cut in the tail. (idToken is made from a tokenResponse.idToken, which is made from json, which is made from data, but I parsed it elsewhere and it has incorrect nonce already)

Question Can you, please, help with this? Maybe you can explain, why this could be happening? Why are returned nonce after tokenExchange in this flow could be cut in a tail? Is it an ios AppAuth library version bug somewhere or a server issue? Is there any clue what is the reason to be cut in a tail could be? Thank you

Expected behavior Expecting nonces to be same after tokenExchange

Environment

  • Device: iPhone XSMAX
  • OS: iOS 16.0
  • AppAuth s.version = "1.6.0"

Maxatma avatar Nov 06 '22 05:11 Maxatma

any updates on this? I have the same issue

maverick-2013 avatar Jun 01 '23 01:06 maverick-2013

It's hard for me to say why this is happening. A couple of ideas off the top of my head that could be helpful:

  1. Is it possible that the nonce is getting modified by mistake prior to the comparison?
  2. Perhaps reach out to your IdP with a question about the truncation. For example, one thing that could occur is that they are truncating the nonce you supply because it exceeds some size.

mdmathias avatar Jun 01 '23 22:06 mdmathias