openid / openid/AppAuth-iOS

"Nonce mismatch"

Open
#733 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug triage
Dominant language
Objective-C
Stars
2k
Forks
867
Avg merge
4d 48m
Merged PRs (30d)
1

Description

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"

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read the OIDAuthState and OIDAuthorizationService token-exchange entry points shown in the report, then compare authorizationResponse.request.nonce with tokenResponse.idToken after parsing the returned data. Check whether the truncated nonce is already present in the provider response; done means identifying whether the defect is in AppAuth-iOS or the server and documenting a reproducible conclusion.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, objective-c, swift
Domain
authentication, mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.