angularidentityserver4csrfoidc-client-jsimplicit-flow

Validate state and nonce in oidc-client


what I understood is- oidc-client generates nonce and state and sends it to an authorization server(Identity server 4). This is used to prevent CSRF attack, replay attack.

State and nonce are sent through signinredirect() sample example below

https://auth.azurewebsites.net/Account/Login?
ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3F
client_id%3DLocal%26
redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A4200%252Fauth-callback%252F%26
response_type%3Did_token%2520token%26
scope%3Dopenid%2520profile%2520Api%26
state%3D212ee56661074896aea2b6043d2b8a3f%26
nonce%3D393838b342d543d5910f38cbcab22fa0%26
loginType%3DInternal // my extra params

Issue 1 - state is undefined after callback

State is added to callback URL as below

    http://localhost:4200/auth-callback#id_token=eyJhbG...
    token_type=Bearer
    &expires_in=300&
    scope=openid%20profile%20Api&
    state=155e3e4352814ca48e127547c134144e&
    session_state=DPXW-ijMR4ST9iTSxgMwhsLq7aoknEZOnq3aFDooCFg.ifImJurwkwU6M5lwZXCUuw

State must be present in user. But in my case, I see the state as undefined in the callback method

  async completeAuthentication() {
    await this.manager
      .signinRedirectCallback()
      .then(x => {
        this.user = x;
        this.user.state = x.state; // undefined
        this.user.session_state = x.session_state;
      })
      .catch(errorData => {
        const expired = errorData;
      });

state is undefined but session state has value

Question --

  1. Where does oidc store state after generation?
  2. Why is state undefined? How to retrieve state after callback? I guess not by URL(path)!
  3. Does oidc internally validates state? How? Where?

Issue 2 - nonce

nonce value is received in id_token

created: 1594171097
extraTokenParams: {}
id: "5cc732d3b7fe4a0abdb371be3bda69a6"
nonce: "17c3f171328b4542a282fcbdd43d6fe4"

Also I see there are 2-4 oidc user are stored in local storage after login. why so? They have same user info but different ID and nonce. I user clearstalestate() to these all are generated after each fresh login or refresh enter image description here

Questions -

  1. Why 2-4 user info is stored in local storage? which method generates the local storage user?
  2. The nonce value is per session or per user request?
  3. Where does the nonce value stored after generation?
  4. Does oidc validates nonce internally? Where? If not how should I do it?

Solution

  • So I have debugged the code and found the questions for your answers,

    Now coming back to state param validation. It is no longer available in User object, instead it is validated before hand internally. Here is the code extract for that from oidc-client js

    processSigninResponse(url, stateStore) {
        Log.debug("OidcClient.processSigninResponse");
    
        var response = new SigninResponse(url);
    
        if (!response.state) {
            Log.error("OidcClient.processSigninResponse: No state in response");
            return Promise.reject(new Error("No state in response"));
        }
    
        stateStore = stateStore || this._stateStore;
    
        return stateStore.remove(response.state).then(storedStateString => {
            if (!storedStateString) {
                Log.error("OidcClient.processSigninResponse: No matching state found in storage");
                throw new Error("No matching state found in storage");
            }
    
            let state = SigninState.fromStorageString(storedStateString);
    
            Log.debug("OidcClient.processSigninResponse: Received state from storage; validating response");
            return this._validator.validateSigninResponse(state, response);
        });
    }
    

    Both, state and nonce are managed by oidc-client library.