import { TestBed } from '@/testing';
import {
  mockImplementationWhenArgs,
  mockImplementationWhenArgsEqual,
} from '@/testing/spy';
import { firstValueFrom, of } from 'rxjs';
import { vi } from 'vitest';
import type { AuthWellKnownEndpoints } from '../config/auth-well-known/auth-well-known-endpoints';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import type { CallbackContext } from '../flows/callback-context';
import { LogLevel } from '../logging/log-level';
import { LoggerService } from '../logging/logger.service';
import { StoragePersistenceService } from '../storage/storage-persistence.service';
import { mockProvider } from '../testing/mock';
import { EqualityService } from '../utils/equality/equality.service';
import { FlowHelper } from '../utils/flowHelper/flow-helper.service';
import { TokenHelperService } from '../utils/tokenHelper/token-helper.service';
import { StateValidationService } from './state-validation.service';
import { TokenValidationService } from './token-validation.service';
import { ValidationResult } from './validation-result';

describe('State Validation Service', () => {
  let stateValidationService: StateValidationService;
  let tokenValidationService: TokenValidationService;
  let tokenHelperService: TokenHelperService;
  let loggerService: LoggerService;
  let config: OpenIdConfiguration;
  let authWellKnownEndpoints: AuthWellKnownEndpoints;
  let storagePersistenceService: StoragePersistenceService;
  let flowHelper: FlowHelper;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        StateValidationService,
        mockProvider(StoragePersistenceService),
        mockProvider(TokenValidationService),
        mockProvider(LoggerService),
        TokenHelperService,
        EqualityService,
        FlowHelper,
      ],
    });
    stateValidationService = TestBed.inject(StateValidationService);
    tokenValidationService = TestBed.inject(TokenValidationService);
    tokenHelperService = TestBed.inject(TokenHelperService);
    loggerService = TestBed.inject(LoggerService);
    storagePersistenceService = TestBed.inject(StoragePersistenceService);
    flowHelper = TestBed.inject(FlowHelper);
    config = {
      authority: 'https://localhost:44363',
      redirectUrl: 'https://localhost:44363',
      clientId: 'singleapp',
      responseType: 'id_token token',
      scope: 'dataEventRecords openid',
      postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
      startCheckSession: false,
      silentRenew: true,
      silentRenewUrl: 'https://localhost:44363/silent-renew.html',
      postLoginRoute: '/dataeventrecords',
      forbiddenRoute: '/Forbidden',
      unauthorizedRoute: '/Unauthorized',
      logLevel: LogLevel.Debug,
      maxIdTokenIatOffsetAllowedInSeconds: 10,
    };

    authWellKnownEndpoints = {
      issuer: 'https://localhost:44363',
      jwksUri: 'https://localhost:44363/well-known/openid-configuration/jwks',
      authorizationEndpoint: 'https://localhost:44363/connect/authorize',
      tokenEndpoint: 'https://localhost:44363/connect/token',
      userInfoEndpoint: 'https://localhost:44363/connect/userinfo',
      endSessionEndpoint: 'https://localhost:44363/connect/endsession',
      checkSessionIframe: 'https://localhost:44363/connect/checksession',
      revocationEndpoint: 'https://localhost:44363/connect/revocation',
      introspectionEndpoint: 'https://localhost:44363/connect/introspect',
    };
  });

  it('should create', () => {
    expect(stateValidationService).toBeTruthy();
    expect(tokenValidationService).toBeTruthy();
  });

  describe('isIdTokenAfterRefreshTokenRequestValid', () => {
    it('validate refresh good ', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: true,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: idToken,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.com/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f5',
        aud: 'f1934a6e-958d-4198-9f36-6127cfc4cdb3',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206486,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(true);
    });

    it('validate refresh invalid iss ', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: true,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: idToken,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.ch/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f5',
        aud: 'f1934a6e-958d-4198-9f36-6127cfc4cdb3',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206486,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(false);
    });

    it('validate refresh invalid sub ', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: true,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: idToken,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.com/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f7',
        aud: 'f1934a6e-958d-4198-9f36-6127cfc4cdb3',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206486,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(false);
    });

    it('validate refresh invalid auth_time ', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: false,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: idToken,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.com/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f5',
        aud: 'f1934a6e-958d-4198-9f36-6127cfc4cdb3',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206488,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(false);
    });

    it('validate refresh good full', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: false,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: idToken,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.com/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f5',
        aud: 'f1934a6e-958d-4198-9f36-6127cfc4cdb3',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206486,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(true);
    });

    it('validate refresh good no existing id_token', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: false,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: null,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.com/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f5',
        aud: 'f1934a6e-958d-4198-9f36-6127cfc4cdb3',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206486,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(true);
    });

    it('validate refresh invalid aud ', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: false,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: idToken,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.com/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f5',
        aud: 'bad',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206488,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(false);
    });

    it('validate refresh invalid azp ', () => {
      const accessToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJpc3MiOiJodHRwczovL2RhbWllbmJvZC5iMmNsb2dpbi5jb20vYTA5NThmNDUtMTk1Yi00MDM2LTkyNTktZGUyZjdlNTk0ZGI2L3YyLjAvIiwiZXhwIjoxNTg5MjEwMDg2LCJuYmYiOjE1ODkyMDY0ODYsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsIm5hbWUiOiJkYW1pZW5ib2QiLCJlbWFpbHMiOlsiZGFtaWVuQGRhbWllbmJvZC5vbm1pY3Jvc29mdC5jb20iXSwidGZwIjoiQjJDXzFfYjJjcG9saWN5ZGFtaWVuIiwibm9uY2UiOiIwMDdjNDE1M2I2YTA1MTdjMGU0OTc0NzZmYjI0OTk0OGVjNWNsT3ZRUSIsInNjcCI6ImRlbW8ucmVhZCIsImF6cCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsInZlciI6IjEuMCIsImlhdCI6MTU4OTIwNjQ4Nn0.Zyg8GAsyj8_ljdheJ57oQ8ldZMon4nLs1VCkBnIon2cXGrXlTA_fYP_Ypf5x5OZcCg-wXdo9RttsLRD69v1cnd5eUc9crzkJ18BruRdhoVQdlrGuakwKujozY2-EU8KNH64qSDpPOqQ9m4jdzGAOkY0wWitOlvYoNZHDzDS4ZIWn8W5H2nwAbf8LMAdXqy41YaIBF4lo3ZaKoUKQqCwIG_0aLvRQcmiwkEoQ5-EUb_hdOejTIbIT5PryyqMnvJYgyrKTf1VY060YpETH19PMosNriwPrPesJhsruphqzaJexg0Pt09ILoMHJhebkON-oPjXLjDOGLfnRTPp6oP_Drg';
      const idToken =
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
      const refreshTokenData =
        'eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAifQ..Gn8_Hs0IAsJm7Tlw.4dvuowpuUHz2RifIINXM5mBbiOorKgAWZapLdohY9LYd4yxAr-K2E8PFCi_lmbTfY0nxXkRqL9S_JnJKP_2Sd_R0g3PC5weu9XxGIT-oWATtkVX4KDWlAsN0-xWUosulT4LEbFygC3bA6B5Ch2BgN_zZ5L-aJjwE1JkE55tQCDgT2tS6uRQjvh1U3ddWgYEsmCqbWQnwbMPPkxA-PvXXTtUKqXTzAo0T9tLBXrSaXurq0Y-visy036Sy9Y7f-duiTLMJ8WKw_XYz3uzsj7Y0SV2A3m2rJNs3HjPBRUOyyWpdhmjo3VAes1bc8nZuZHsP4S2HSe7hRoOxYkWfGhIBvI8FT3dBZKfttAT64fsR-fQtQ4ia0z12SsLoCJhF1VRf3NU1-Lc2raP0kvN7HOGQFuVPkjmWOqKKoy4at7PAvC_sWHOND7QkmYkFyfQvGcNmt_lA10VZlr_cOeuiNCTPUHZHi-pv7nsefxVoPYGJPztGvIJ_daAUigXMZGARTTIhCt84PzPEdPMlCSI3GuNxQoD95rhvSyZP8SBQ5NIs_qwxYMAfzXgJP8aFK-ZHd8ZQfm1Rg79mO0LH1GcQzIhc4pC4PsvcSm6I6Jo1ZeEw5pRQQWf59asPyORG-2qfnMvZB1hGCZU7J78lAcse6sXCtBlQDLe9Th5Goibn.XdCGzjyrmgKzJktSPSDH0g';

      const configRefresh = {
        authority: 'https://localhost:44363',
        redirectUrl: 'https://localhost:44363',
        clientId: 'singleapp',
        responseType: 'icode',
        scope: 'dataEventRecords openid',
        postLogoutRedirectUri: 'https://localhost:44363/Unauthorized',
        startCheckSession: false,
        silentRenew: true,
        silentRenewUrl: 'https://localhost:44363/silent-renew.html',
        postLoginRoute: '/dataeventrecords',
        forbiddenRoute: '/Forbidden',
        unauthorizedRoute: '/Unauthorized',
        logLevel: LogLevel.Debug,
        maxIdTokenIatOffsetAllowedInSeconds: 10,
        useRefreshToken: true,
        ignoreNonceAfterRefresh: true,
        disableRefreshIdTokenAuthTimeValidation: false,
        triggerRefreshWhenIdTokenExpired: true,
      };

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: refreshTokenData,
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: idToken,
        authResult: {
          access_token: accessToken,
          id_token: idToken,
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const decodedIdToken = {
        exp: 1589210086,
        nbf: 1589206486,
        ver: '1.0',
        iss: 'https://damienbod.b2clogin.com/a0958f45-195b-4036-9259-de2f7e594db6/v2.0/',
        sub: 'f836f380-3c64-4802-8dbc-011981c068f5',
        aud: 'f1934a6e-958d-4198-9f36-6127cfc4cdb3',
        nonce: '007c4153b6a0517c0e497476fb249948ec5clOvQQ',
        iat: 1589206486,
        auth_time: 1589206488,
        name: 'damienbod',
        emails: ['damien@damienbod.onmicrosoft.com'],
        tfp: 'B2C_1_b2cpolicydamien',
        at_hash: 'Zk0fKJS_pYhOpM8IBa12fw',
        azp: 'no bad',
      };
      const isValid = (
        stateValidationService as any
      ).isIdTokenAfterRefreshTokenRequestValid(
        callbackContext,
        decodedIdToken,
        configRefresh
      );

      expect(isValid).toBe(false);
    });
  });

  describe('getValidatedStateResult', () => {
    it('should return authResponseIsValid false when null is passed', async () => {
      const isValidObs$ = stateValidationService.getValidatedStateResult(
        {} as CallbackContext,
        config
      );

      const isValid = await firstValueFrom(isValidObs$);
      expect(isValid.authResponseIsValid).toBeFalsy();
    });

    it('should return invalid context error', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);

      config.responseType = 'id_token token';

      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        false
      );

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: null,
        authResult: {
          error: 'access_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const isValidObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const isValid = await firstValueFrom(isValidObs$);
      expect(isValid.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateIdTokenExpNotExpired is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);

      config.responseType = 'id_token token';

      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );

      vi.spyOn(tokenValidationService, 'hasIdTokenExpired').mockReturnValue(
        false
      );
      vi.spyOn(
        tokenValidationService,
        'validateAccessTokenNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpValid'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenAtHash').mockReturnValue(
        of(true)
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );

      config.clientId = '';
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenExpNotExpired'
      ).mockReturnValue(false);
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );

      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback id token expired'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateStateFromHashCallback is false', async () => {
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(false);

      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      expect(
        tokenValidationService.validateStateFromHashCallback
      ).toHaveBeenCalled();

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback incorrect state'
      );
      expect(state.accessToken).toBe('');
      expect(state.authResponseIsValid).toBe(false);
      expect(state.decodedIdToken).toBeDefined();
      expect(state.idToken).toBe('');
    });

    it('access_token should equal result.access_token and is valid if response_type is "id_token token"', async () => {
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'hasIdTokenExpired').mockReturnValue(
        false
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateAccessTokenNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpValid'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenExpNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAtHash').mockReturnValue(
        of(true)
      );

      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      config.clientId = '';
      config.autoCleanStateAfterAuthentication = false;
      config.responseType = 'id_token token';

      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(true);
    });

    it('should return invalid result if validateSignatureIdToken is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(false));

      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      const logDebugSpy = vi
        .spyOn(loggerService, 'logDebug')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };

      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logDebugSpy.mock.calls).toEqual([
        [config, 'authCallback Signature validation failed id_token'],
        [config, 'authCallback token(s) invalid'],
      ]);
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateIdTokenNonce is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        false
      );
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );

      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback incorrect nonce, did you call the checkAuth() method multiple times?'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateRequiredIdToken is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);

      config.responseType = 'id_token token';

      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );

      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));

      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );

      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(false);
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logDebugSpy = vi
        .spyOn(loggerService, 'logDebug')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logDebugSpy).toHaveBeenCalledWith(
        config,
        'authCallback Validation, one of the REQUIRED properties missing from id_token'
      );
      expect(logDebugSpy).toHaveBeenCalledWith(
        config,
        'authCallback token(s) invalid'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateIdTokenIatMaxOffset is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);

      config.responseType = 'id_token token';

      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(false);

      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback Validation, iat rejected id_token was issued too far away from the current time'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateIdTokenIss is false and has authWellKnownEndPoints', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);

      config.responseType = 'id_token token';

      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );

      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));

      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );

      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);

      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);

      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        false
      );
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback incorrect iss does not match authWellKnownEndpoints issuer'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateIdTokenIss is false and has no authWellKnownEndPoints', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => null
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authWellKnownEndpoints is undefined'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
      expect(state.state).toBe(ValidationResult.NoAuthWellKnownEndPoints);
    });

    it('should return invalid result if validateIdTokenAud is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        false
      );

      config.clientId = '';
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback incorrect aud'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return invalid result if validateIdTokenAzpExistsIfMoreThanOneAud is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(false);

      config.clientId = '';
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback missing azp'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
      expect(state.state).toBe(ValidationResult.IncorrectAzp);
    });

    it('should return invalid result if validateIdTokenAzpValid is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpValid'
      ).mockReturnValue(false);

      config.clientId = '';
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback incorrect azp'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
      expect(state.state).toBe(ValidationResult.IncorrectAzp);
    });

    it('should return invalid result if isIdTokenAfterRefreshTokenRequestValid is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpValid'
      ).mockReturnValue(true);
      vi.spyOn(
        stateValidationService as any,
        'isIdTokenAfterRefreshTokenRequestValid'
      ).mockReturnValue(false);

      config.clientId = '';
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );
      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback pre, post id_token claims do not match in refresh'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
      expect(state.state).toBe(
        ValidationResult.IncorrectIdTokenClaimsAfterRefresh
      );
    });

    it('Reponse is valid if authConfiguration.response_type does not equal "id_token token"', async () => {
      vi.spyOn(tokenValidationService, 'hasIdTokenExpired').mockReturnValue(
        false
      );
      vi.spyOn(
        tokenValidationService,
        'validateAccessTokenNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpValid'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenAtHash').mockReturnValue(
        of(true)
      );
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      config.clientId = '';
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenExpNotExpired'
      ).mockReturnValue(true);
      config.responseType = 'NOT id_token token';
      config.autoCleanStateAfterAuthentication = false;
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );

      const logDebugSpy = vi
        .spyOn(loggerService, 'logDebug')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };

      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logDebugSpy).toHaveBeenCalledWith(
        config,
        'authCallback token(s) validated, continue'
      );
      expect(logDebugSpy).toHaveBeenCalledWith(
        config,
        'authCallback token(s) invalid'
      );
      expect(state.accessToken).toBe('');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(true);
    });

    it('Response is invalid if validateIdTokenAtHash is false', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      config.clientId = '';
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenExpNotExpired'
      ).mockReturnValue(true);
      config.responseType = 'id_token token';
      config.autoCleanStateAfterAuthentication = false;
      vi.spyOn(tokenValidationService, 'validateIdTokenAtHash').mockReturnValue(
        of(false)
      );

      vi.spyOn(tokenValidationService, 'hasIdTokenExpired').mockReturnValue(
        false
      );
      vi.spyOn(
        tokenValidationService,
        'validateAccessTokenNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpValid'
      ).mockReturnValue(true);

      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );

      const logWarningSpy = vi
        .spyOn(loggerService, 'logWarning')
        .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
        config,
        'authCallback incorrect at_hash'
      );
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('id_tokenTEST');
      expect(state.decodedIdToken).toBe('decoded_id_token');
      expect(state.authResponseIsValid).toBe(false);
    });

    it('should return valid result if validateIdTokenIss is false and iss_validation_off is true', async () => {
      config.issValidationOff = true;
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        false
      );

      vi.spyOn(tokenValidationService, 'hasIdTokenExpired').mockReturnValue(
        false
      );
      vi.spyOn(
        tokenValidationService,
        'validateAccessTokenNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpExistsIfMoreThanOneAud'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenAzpValid'
      ).mockReturnValue(true);

      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenExpNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenAtHash').mockReturnValue(
        of(true)
      );
      config.responseType = 'id_token token';
      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );

      const logDebugSpy = vi.spyOn(loggerService, 'logDebug'); // .mockImplementation(() => undefined);

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: 'id_tokenTEST',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };
      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);

      expect(logDebugSpy.mock.calls).toEqual([
        [config, 'iss validation is turned off, this is not recommended!'],
        [config, 'authCallback token(s) validated, continue'],
      ]);
      expect(state.state).toBe(ValidationResult.Ok);
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.authResponseIsValid).toBe(true);
      expect(state.decodedIdToken).toBeDefined();
      expect(state.idToken).toBe('id_tokenTEST');
    });

    it('should return valid if there is no id_token', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);

      config.responseType = 'code';
      vi.spyOn(tokenHelperService, 'getPayloadFromToken').mockReturnValue(
        'decoded_id_token'
      );
      vi.spyOn(
        tokenValidationService,
        'validateSignatureIdToken'
      ).mockReturnValue(of(true));
      vi.spyOn(tokenValidationService, 'validateIdTokenNonce').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateRequiredIdToken'
      ).mockReturnValue(true);
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      config.clientId = '';
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenIatMaxOffset'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenAud').mockReturnValue(
        true
      );
      vi.spyOn(
        tokenValidationService,
        'validateIdTokenExpNotExpired'
      ).mockReturnValue(true);
      vi.spyOn(tokenValidationService, 'validateIdTokenIss').mockReturnValue(
        true
      );
      vi.spyOn(tokenValidationService, 'validateIdTokenAtHash').mockReturnValue(
        of(true)
      );

      config.autoCleanStateAfterAuthentication = false;

      const readSpy = vi.spyOn(storagePersistenceService, 'read');

      mockImplementationWhenArgsEqual(
        readSpy,
        ['authWellKnownEndPoints', config],
        () => authWellKnownEndpoints
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authStateControl', config],
        () => 'authStateControl'
      );
      mockImplementationWhenArgsEqual(
        readSpy,
        ['authNonce', config],
        () => 'authNonce'
      );

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsdfhhhhsdf',
        sessionState: 'fdffsggggggdfsdf',
        authResult: {
          access_token: 'access_tokenTEST',
          id_token: '',
        },
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
        existingIdToken: null,
      };

      const stateObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const state = await firstValueFrom(stateObs$);
      expect(state.accessToken).toBe('access_tokenTEST');
      expect(state.idToken).toBe('');
      expect(state.decodedIdToken).toBeDefined();
      expect(state.authResponseIsValid).toBe(true);
    });

    it('should return OK if disableIdTokenValidation is true', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      vi.spyOn(
        flowHelper,
        'isCurrentFlowImplicitFlowWithAccessToken'
      ).mockReturnValue(false);
      vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(false);

      config.responseType = 'id_token token';
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      config.disableIdTokenValidation = true;

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: null,
        authResult: {},
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const isValidObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const isValid = await firstValueFrom(isValidObs$);
      expect(isValid.state).toBe(ValidationResult.Ok);
      expect(isValid.authResponseIsValid).toBe(true);
    });

    it('should return OK if disableIdTokenValidation is true', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      vi.spyOn(
        flowHelper,
        'isCurrentFlowImplicitFlowWithAccessToken'
      ).mockReturnValue(false);
      vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(false);

      config.responseType = 'id_token token';
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      config.disableIdTokenValidation = true;

      const callbackContext: CallbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: '',
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: '',
        authResult: {},
        isRenewProcess: false,
        jwtKeys: null,
        validationResult: null,
      };

      const isValidObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const isValid = await firstValueFrom(isValidObs$);
      expect(isValid.state).toBe(ValidationResult.Ok);
      expect(isValid.authResponseIsValid).toBe(true);
    });

    it('should return OK if disableIdTokenValidation is false but inrefreshtokenflow and no id token is returned', async () => {
      vi.spyOn(
        tokenValidationService,
        'validateStateFromHashCallback'
      ).mockReturnValue(true);
      vi.spyOn(
        flowHelper,
        'isCurrentFlowImplicitFlowWithAccessToken'
      ).mockReturnValue(false);
      vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(false);

      config.responseType = 'id_token token';
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
      config.disableIdTokenValidation = false;

      const callbackContext = {
        code: 'fdffsdfsdf',
        refreshToken: 'something',
        state: 'fdffsggggggdfsdf',
        sessionState: 'fdffsggggggdfsdf',
        existingIdToken: null,
        authResult: {},
        isRenewProcess: true,
        jwtKeys: null,
        validationResult: null,
      };

      const isValidObs$ = stateValidationService.getValidatedStateResult(
        callbackContext,
        config
      );

      const isValid = await firstValueFrom(isValidObs$);
      expect(isValid.state).toBe(ValidationResult.Ok);
      expect(isValid.authResponseIsValid).toBe(true);
    });
  });
});
