0

I've an Identity Server 4 app with Angular client using oidc-client. The problem is that in production the method addUserSignedOut doesn't stop to get called after I login in the client. I test it locally and works fine.

I think the problem could be related with the cookies not present on deployment. I check on checksession getCookies() method and cookies are empty (document.cookie = "") after login, any idea how to fix that?

This is my IDP code:

  public void ConfigureServices(IServiceCollection services)
    {
        string connectionString = Configuration.GetConnectionString("DefaultConnection");
        var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;



        services.AddDataProtection()
            .AddKeyManagementOptions(dp => dp.NewKeyLifetime = TimeSpan.FromDays(90));
            //.PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
            //.ProtectKeysWithAzureKeyVault("<keyIdentifier>", "<clientId>", "<clientSecret>");

        services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));

        services.AddIdentity<ApplicationUser, IdentityRole>(options => {
            options.Password.RequiredLength = 6;            // Passwords must be at least 6 characters
            options.Password.RequireLowercase = true;       // Passwords must have at least one lowercase ('a'-'z')
            options.Password.RequireUppercase = true;       // Passwords must have at least one uppercase ('A'-'Z')
            options.Password.RequireDigit = true;           // Passwords must have at least one digit ('0'-'9')
            options.Password.RequireNonAlphanumeric = true; // Passwords must have at least one non alphanumeric character
            options.Password.RequiredUniqueChars = 1;       // Passwords must use at least 1 different characters
        })
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

        services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1).AddXmlSerializerFormatters();

        services.Configure<IISOptions>(iis =>
        {
            iis.AuthenticationDisplayName = "Windows";
            iis.AutomaticAuthentication = false;
        });

        services.AddTransient<IProfileService, ProfileService>();



        var builder = services.AddIdentityServer(
            options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;
            }
        )
        // this adds the config data from DB (clients, resources)
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b =>
                b.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migrationsAssembly));
        })


        // this adds the operational data from DB (codes, tokens, consents)
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b =>
                b.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migrationsAssembly));

            // this enables automatic token cleanup. this is optional.
            options.EnableTokenCleanup = true;
        });


        // Add SAML SSO services.
        services.AddSaml(Configuration.GetSection("SAML"));

        builder.Services.Configure<SecurityStampValidatorOptions>(opts =>
        {
            opts.OnRefreshingPrincipal = SecurityStampValidatorCallback.UpdatePrincipal;
        });

        services.AddAuthentication(options => {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
       .AddCookie("Cookies", opts =>
       {
           opts.SlidingExpiration = true;
           opts.ExpireTimeSpan = TimeSpan.FromMinutes(15);
       });

        if (Environment.IsDevelopment() || Environment.EnvironmentName == "local" )
        {
            builder.AddDeveloperSigningCredential();
        }
        else
        {
            X509Certificate2 cert = null;
            using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                certStore.Open(OpenFlags.ReadOnly);
                X509Certificate2Collection certCollection = certStore.Certificates.Find(
                    X509FindType.FindByThumbprint,
                    Configuration.GetSection("Certificate").GetValue<string>("thumbprint"),
                    false);
                // Get the first cert with the thumbprint
                if (certCollection.Count > 0)
                {
                    cert = certCollection[0];
                    Log.Logger.Information($"Successfully loaded cert from registry: {cert.Thumbprint}");
                }
            }

            // Fallback to local file for development
            if (cert == null)
            {
                throw new Exception("Cannot find any Certificate");
            }
            builder.AddSigningCredential(cert);
        }
    }

    public void Configure(IApplicationBuilder app)
    {
        if (Environment.IsDevelopment() || Environment.EnvironmentName == "local")
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();//SSO
        }

        app.UseHttpsRedirection();//SSO
        app.UseStaticFiles();
        app.UseCookiePolicy();//SSO
        app.UseIdentityServer();
        app.UseAuthentication();//SSO
        app.UseSaml();// Use SAML middleware. 
        app.UseMvcWithDefaultRoute();
    }

IDP logout controller:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout(LogoutInputModel model)
    {
        // build a model so the logged out page knows what to display
        var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);

        if (User?.Identity.IsAuthenticated == true)
        {
            // Request logout at the service provider(SAML).
            await InitiateSingleLogout();

            // delete local authentication cookie
            await _signInManager.SignOutAsync();

            // raise the logout event
            await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
        }

        if (vm.PostLogoutRedirectUri != null) {
            return Redirect(vm.PostLogoutRedirectUri);
        }

         // since we don't have a valid context, then we just go back to the home page
        return Redirect("~/");

    }

SAML logout:

    private async Task InitiateSingleLogout()
    {
        var ssoState = await _samlIdentityProvider.GetStatusAsync();
        if (await ssoState.CanSloAsync())
        {
            // Request logout at the service provider(s).
            await _samlIdentityProvider.InitiateSloAsync();
        }
     }

Angular client oidc-client settings:

const settings: any = {
  authority: `${environment.identity_server_url}`,
  client_id: 'js',
  redirect_uri: `${environment.login_redirect}/signin-oidc`,
  response_type: 'id_token token',
  scope: 'openid profile salesforce api1',
  post_logout_redirect_uri: `${environment.login_redirect}`,
  userStore: new WebStorageStateStore({ store: window.localStorage }),
  silent_redirect_uri: `${environment.login_redirect}/signin-oidc-silent`,
  // automaticSilentRenew: true
};

Handle logout event:

 this.mgr.events.addUserSignedOut(() => {
  this.startSigninMainWindow({ external_logout: true })
 })

This is my guard where I check if the user is Logged in and call signinRedirect():

export class AuthGuardService implements CanActivate {

  constructor(
    private authService: AuthService
  ) { }
  canActivate() {
    const isLoggedIn = this.authService.isLoggedInObs();
    isLoggedIn.subscribe(loggedin => {
      if (!loggedin) {
        this.authService.startSigninMainWindow();
      }
    });
    return isLoggedIn;

  }

}

This is the AuthService class where all the logic about the oidc-client lie.

export class AuthService {
  mgr: UserManager = new UserManager(settings);
  userLoadededEvent: EventEmitter<User> = new EventEmitter<User>();
  currentUser: User;
  loggedIn = false;
  authHeaders: Headers;

  private accountID: string;
  private accountToken: string;
  private internInfoSubject = new BehaviorSubject<InternUser>(null);
  public readonly internInfo$ = this.internInfoSubject
    .asObservable()
    .pipe(
      filter(user => user !== null),
      distinctUntilChanged()
      );

  constructor(
    private router: Router,
    private internPortalService: InternPortalService,
    private apiService: ApiService,
    private http: HttpClient
  ) {

    this.mgr.events.addUserSignedOut(() => {
      this.startSigninMainWindow({ external_logout: true })
    })
  }

  getUser() {
    this.mgr
      .getUser()
      .then(user => {
        console.log('got user', user);
        this.currentUser = user;
        this.userLoadededEvent.emit(user);
      })
      .catch(function(err) {
        console.log(err);
      });
  }

  startSigninMainWindow(query = {}) {
    this.mgr.signinRedirect({extraQueryParams: query});
  }

  endSigninMainWindow() {
    this.mgr
      .signinRedirectCallback()
      .then(user => {
        console.log('signed in', user);
        this.currentUser = user;
        this.setLocalStorage();
      })
      .catch(function(err) {
        console.log(err);
      });
  }

  endSigninSilentMainWindow() {
    this.mgr
      .signinSilentCallback()
      .then(() => {
        console.log('end silent signed in');
      })
      .catch(function(err) {
        console.log(err);
      });
  }

  changePassword(): Observable<any> {
    const params = new HttpParams()
      .set('userId', this.currentUser.profile.sub)
      .set('passwordAction', '2')
      .set('redirectUrl', window.location.href);
    return this.apiService.getIdentityServer('/Token', params);
  }

  startSignoutMainWindow() {
    this.mgr
      .signoutRedirect()
      .then(function(resp) {
        console.log('signed out', resp);
        // setTimeout(5000, () => {
        //   console.log('testing to see if fired...');

        // });
      })
      .catch(function(err) {
        console.log(err);
      });
  }

  isLoggedInObs(): Observable<boolean> {
    return observableFrom(this.mgr.getUser()).pipe(
      map<User, boolean>(user => {
        if (user) {
          if (!this.internPortalService.getAccountId()) {
            this.currentUser = user;
            this.setLocalStorage();
          }
          return true;
        } else {
          return false;
        }
      })
    );
  }

  onSignOut(callback: Function){
    this.mgr.events.addUserSignedOut(resp => {
      console.log("user signed out");
      callback();
    });
    }

  get authenticationInfo(): InternUser {
    return this.internInfoSubject.value;
  }

  private setLocalStorage() {
    this.accountID = this.currentUser.profile.account_id;
    this.accountToken = this.currentUser.access_token;
    this.internPortalService.setAccountId(this.accountID, this.accountToken);
    this.internPortalService
      .getIntern()
      .subscribe(res => this.updateToken(res));
  }

  updateToken(internUser: any): void {
    console.log(internUser);
    if (!internUser) {
      return;
    }
    // TODO: Refactor once BE migration is completed
    internUser.auth = this.currentUser;
    internUser.state_locales = this.internPortalService.getStateLocales(
      internUser.state
    );
    this.internInfoSubject.next(internUser);
    this.router.navigate(['/intern-portal']);
  }

  // TODO: To be removed after QA
  public updateLocale(counter: number): void {
    const stateList = ['TX', 'MI', 'AZ', 'NC', 'SC', 'FL', 'NV', 'IN'];
    const newUser = this.authenticationInfo;
    newUser.state = stateList[counter];
    newUser.state_locales = this.internPortalService.getStateLocales(
      newUser.state
    );
    console.log(`An user from: ${newUser.state_locales.state} was loaded`);
    this.internInfoSubject.next(newUser);
  }

  logout(): void {
    localStorage.clear();
    this.internInfoSubject.next(null);
  }
}

And the signin-oidc-silent component:

export class SigninOidcComponent implements OnInit {
  private element: any;

  constructor(private authService: AuthService) {
  }

  ngOnInit() {
    console.log('init oidc logic');
    this.authService.endSigninMainWindow();
  }

}

I just don't know why the infinite loop happens on the hosted apps. Actually if I use the client locally and point to my hosted IDP I get the same infinite loop.

I don't see any error on console, only this warning ResponseValidator._processSigninParams: Response was error login_required

Here some IDP logs:

2019-04-15 11:16:49.383 +00:00 [DBG] Request path /connect/authorize matched to endpoint type Authorize
2019-04-15 11:16:49.418 +00:00 [DBG] Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint
2019-04-15 11:16:49.419 +00:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
2019-04-15 11:16:49.423 +00:00 [DBG] Start authorize request
2019-04-15 11:16:49.435 +00:00 [DBG] User in authorize request: cfd7214d-305d-4d91-9d50-671dac6b37d8
2019-04-15 11:16:49.436 +00:00 [DBG] Start authorize request protocol validation
2019-04-15 11:16:49.566 +00:00 [DBG] js found in database: true
2019-04-15 11:16:49.569 +00:00 [DBG] client configuration validation for client js succeeded.
2019-04-15 11:16:49.624 +00:00 [DBG] Found ["openid","profile","salesforce"] identity scopes in database
2019-04-15 11:16:49.673 +00:00 [DBG] Found ["api1"] API scopes in database
2019-04-15 11:16:49.687 +00:00 [DBG] Found ["openid","profile","salesforce"] identity scopes in database
2019-04-15 11:16:49.703 +00:00 [DBG] Found ["api1"] API scopes in database
2019-04-15 11:16:49.712 +00:00 [DBG] Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator
2019-04-15 11:16:49.755 +00:00 [INF] ValidatedAuthorizeRequest
{"ClientId":"js","ClientName":"JavaScript Client","RedirectUri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc","AllowedRedirectUris":["http://localhost:5003/callback.html","http://localhost:5003/signin-oidc","http://localhost:4200/signin-oidc","http://localhost:4200/signin-oidc-silent","https://tt-prince-internportal.azurewebsites.net/signin-oidc","https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","https://tt-prince-ip-fe-appservice-qa.azurewebsites.net/signin-oidc","https://tt-prince-ip-fe-appservice-qa.azurewebsites.net/signin-oidc-silent"],"SubjectId":"cfd7214d-305d-4d91-9d50-671dac6b37d8","ResponseType":"id_token token","ResponseMode":"fragment","GrantType":"implicit","RequestedScopes":"openid profile salesforce api1","State":"42a8d9eda2a94c61a4f8ac2f13bbee52","UiLocales":null,"Nonce":"76c3d45a35524c908b1593454305a796","AuthenticationContextReferenceClasses":null,"DisplayMode":null,"PromptMode":null,"MaxAge":null,"LoginHint":null,"SessionId":"a593b46f1553fa4d5beae979ec6475ba","Raw":{"client_id":"js","redirect_uri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc","response_type":"id_token token","scope":"openid profile salesforce api1","state":"42a8d9eda2a94c61a4f8ac2f13bbee52","nonce":"76c3d45a35524c908b1593454305a796","external_logout":"true"},"$type":"AuthorizeRequestValidationLog"}
2019-04-15 11:16:49.758 +00:00 [DBG] Client is configured to not require consent, no consent is required
2019-04-15 11:16:49.759 +00:00 [DBG] Creating Implicit Flow response.
2019-04-15 11:16:49.760 +00:00 [DBG] Getting claims for access token for client: js
2019-04-15 11:16:49.765 +00:00 [DBG] Getting claims for access token for subject: cfd7214d-305d-4d91-9d50-671dac6b37d8
2019-04-15 11:16:49.769 +00:00 [DBG] Claim types from profile service that were filtered: ["sub","idp","amr","auth_time"]
2019-04-15 11:16:49.812 +00:00 [DBG] Getting claims for identity token for subject: cfd7214d-305d-4d91-9d50-671dac6b37d8 and client: js
2019-04-15 11:16:49.814 +00:00 [DBG] In addition to an id_token, an access_token was requested. No claims other than sub are included in the id_token. To obtain more user claims, either use the user info endpoint or set AlwaysIncludeUserClaimsInIdToken on the client configuration.
2019-04-15 11:16:49.835 +00:00 [INF] {"ClientId":"js","ClientName":"JavaScript Client","RedirectUri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc","Endpoint":"Authorize","SubjectId":"cfd7214d-305d-4d91-9d50-671dac6b37d8","Scopes":"openid profile salesforce api1","GrantType":"implicit","Tokens":[{"TokenType":"id_token","TokenValue":"****UIMg","$type":"Token"},{"TokenType":"access_token","TokenValue":"****NWSw","$type":"Token"}],"Category":"Token","Name":"Token Issued Success","EventType":"Success","Id":2000,"Message":null,"ActivityId":"0HLLVFNQC24O5:00000001","TimeStamp":"2019-04-15T11:16:49.0000000Z","ProcessId":11056,"LocalIpAddress":"127.0.0.1:28158","RemoteIpAddress":"167.57.108.74","$type":"TokenIssuedSuccessEvent"}
2019-04-15 11:16:49.845 +00:00 [INF] Authorize endpoint response
{"SubjectId":"cfd7214d-305d-4d91-9d50-671dac6b37d8","ClientId":"js","RedirectUri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc","State":"42a8d9eda2a94c61a4f8ac2f13bbee52","Scope":"openid profile salesforce api1","Error":null,"ErrorDescription":null,"$type":"AuthorizeResponseLog"}
2019-04-15 11:16:56.059 +00:00 [DBG] CORS request made for path: /.well-known/openid-configuration from origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:56.067 +00:00 [DBG] Origin https://tt-prince-internportal.azurewebsites.net is allowed: true
2019-04-15 11:16:56.069 +00:00 [DBG] CorsPolicyService allowed origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:56.071 +00:00 [DBG] Request path /.well-known/openid-configuration matched to endpoint type Discovery
2019-04-15 11:16:56.076 +00:00 [DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint
2019-04-15 11:16:56.077 +00:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
2019-04-15 11:16:56.079 +00:00 [DBG] Start discovery request
2019-04-15 11:16:56.118 +00:00 [DBG] Found ["openid","profile","salesforce","role","api1"] as all scopes in database
2019-04-15 11:16:56.341 +00:00 [DBG] CORS request made for path: /.well-known/openid-configuration from origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:56.347 +00:00 [DBG] Origin https://tt-prince-internportal.azurewebsites.net is allowed: true
2019-04-15 11:16:56.349 +00:00 [DBG] CorsPolicyService allowed origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:56.350 +00:00 [DBG] Request path /.well-known/openid-configuration matched to endpoint type Discovery
2019-04-15 11:16:56.351 +00:00 [DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint
2019-04-15 11:16:56.352 +00:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
2019-04-15 11:16:56.353 +00:00 [DBG] Start discovery request
2019-04-15 11:16:56.374 +00:00 [DBG] Found ["openid","profile","salesforce","role","api1"] as all scopes in database
2019-04-15 11:16:56.381 +00:00 [DBG] Request path /connect/checksession matched to endpoint type Checksession
2019-04-15 11:16:56.389 +00:00 [DBG] Endpoint enabled: Checksession, successfully created handler: IdentityServer4.Endpoints.CheckSessionEndpoint
2019-04-15 11:16:56.390 +00:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.CheckSessionEndpoint for /connect/checksession
2019-04-15 11:16:56.392 +00:00 [DBG] Rendering check session result
2019-04-15 11:16:56.613 +00:00 [DBG] CORS request made for path: /.well-known/openid-configuration/jwks from origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:56.618 +00:00 [DBG] Origin https://tt-prince-internportal.azurewebsites.net is allowed: true
2019-04-15 11:16:56.621 +00:00 [DBG] CorsPolicyService allowed origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:56.622 +00:00 [DBG] Request path /.well-known/openid-configuration/jwks matched to endpoint type Discovery
2019-04-15 11:16:56.624 +00:00 [DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryKeyEndpoint
2019-04-15 11:16:56.625 +00:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryKeyEndpoint for /.well-known/openid-configuration/jwks
2019-04-15 11:16:56.628 +00:00 [DBG] Start key discovery request
2019-04-15 11:16:56.719 +00:00 [DBG] Request path /connect/authorize matched to endpoint type Authorize
2019-04-15 11:16:56.722 +00:00 [DBG] Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint
2019-04-15 11:16:56.723 +00:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
2019-04-15 11:16:56.725 +00:00 [DBG] Start authorize request
2019-04-15 11:16:56.726 +00:00 [DBG] No user present in authorize request
2019-04-15 11:16:56.727 +00:00 [DBG] Start authorize request protocol validation
2019-04-15 11:16:56.743 +00:00 [DBG] js found in database: true
2019-04-15 11:16:56.748 +00:00 [DBG] client configuration validation for client js succeeded.
2019-04-15 11:16:56.757 +00:00 [DBG] Found ["openid"] identity scopes in database
2019-04-15 11:16:56.760 +00:00 [DBG] Found [] API scopes in database
2019-04-15 11:16:56.767 +00:00 [DBG] Found ["openid"] identity scopes in database
2019-04-15 11:16:56.772 +00:00 [DBG] Found [] API scopes in database
2019-04-15 11:16:56.773 +00:00 [DBG] Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator
2019-04-15 11:16:56.775 +00:00 [INF] ValidatedAuthorizeRequest
{"ClientId":"js","ClientName":"JavaScript Client","RedirectUri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","AllowedRedirectUris":["http://localhost:5003/callback.html","http://localhost:5003/signin-oidc","http://localhost:4200/signin-oidc","http://localhost:4200/signin-oidc-silent","https://tt-prince-internportal.azurewebsites.net/signin-oidc","https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","https://tt-prince-ip-fe-appservice-qa.azurewebsites.net/signin-oidc","https://tt-prince-ip-fe-appservice-qa.azurewebsites.net/signin-oidc-silent"],"SubjectId":"anonymous","ResponseType":"id_token","ResponseMode":"fragment","GrantType":"implicit","RequestedScopes":"openid","State":"170ef6a2eda34bc6a7c0a5d79fc25a7a","UiLocales":null,"Nonce":"1f0b1599850a4c6882f9e69ceda252ee","AuthenticationContextReferenceClasses":null,"DisplayMode":null,"PromptMode":"none","MaxAge":null,"LoginHint":null,"SessionId":null,"Raw":{"client_id":"js","redirect_uri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","response_type":"id_token","scope":"openid","state":"170ef6a2eda34bc6a7c0a5d79fc25a7a","nonce":"1f0b1599850a4c6882f9e69ceda252ee","prompt":"none"},"$type":"AuthorizeRequestValidationLog"}
2019-04-15 11:16:56.776 +00:00 [INF] Showing error: prompt=none was requested but user is not authenticated
2019-04-15 11:16:56.778 +00:00 [INF] {"ClientId":"js","ClientName":"JavaScript Client","RedirectUri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","AllowedRedirectUris":["http://localhost:5003/callback.html","http://localhost:5003/signin-oidc","http://localhost:4200/signin-oidc","http://localhost:4200/signin-oidc-silent","https://tt-prince-internportal.azurewebsites.net/signin-oidc","https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","https://tt-prince-ip-fe-appservice-qa.azurewebsites.net/signin-oidc","https://tt-prince-ip-fe-appservice-qa.azurewebsites.net/signin-oidc-silent"],"SubjectId":"anonymous","ResponseType":"id_token","ResponseMode":"fragment","GrantType":"implicit","RequestedScopes":"openid","State":"170ef6a2eda34bc6a7c0a5d79fc25a7a","UiLocales":null,"Nonce":"1f0b1599850a4c6882f9e69ceda252ee","AuthenticationContextReferenceClasses":null,"DisplayMode":null,"PromptMode":"none","MaxAge":null,"LoginHint":null,"SessionId":null,"Raw":{"client_id":"js","redirect_uri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","response_type":"id_token","scope":"openid","state":"170ef6a2eda34bc6a7c0a5d79fc25a7a","nonce":"1f0b1599850a4c6882f9e69ceda252ee","prompt":"none"},"$type":"AuthorizeRequestValidationLog"}
2019-04-15 11:16:56.779 +00:00 [INF] {"ClientId":"js","ClientName":"JavaScript Client","RedirectUri":"https://tt-prince-internportal.azurewebsites.net/signin-oidc-silent","Endpoint":"Authorize","SubjectId":null,"Scopes":"openid","GrantType":"implicit","Error":"login_required","ErrorDescription":null,"Category":"Token","Name":"Token Issued Failure","EventType":"Failure","Id":2001,"Message":null,"ActivityId":"0HLLVFNQC24O6:00000002","TimeStamp":"2019-04-15T11:16:56.0000000Z","ProcessId":11056,"LocalIpAddress":"127.0.0.1:28158","RemoteIpAddress":"167.57.108.74","$type":"TokenIssuedFailureEvent"}
2019-04-15 11:16:57.174 +00:00 [DBG] CORS request made for path: /connect/userinfo from origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:57.178 +00:00 [DBG] Origin https://tt-prince-internportal.azurewebsites.net is allowed: true
2019-04-15 11:16:57.179 +00:00 [DBG] CorsPolicyService allowed origin: https://tt-prince-internportal.azurewebsites.net
2019-04-15 11:16:57.180 +00:00 [DBG] Request path /connect/userinfo matched to endpoint type Userinfo
2019-04-15 11:16:57.185 +00:00 [DBG] Endpoint enabled: Userinfo, successfully created handler: IdentityServer4.Endpoints.UserInfoEndpoint
2019-04-15 11:16:57.186 +00:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.UserInfoEndpoint for /connect/userinfo
2019-04-15 11:16:57.188 +00:00 [DBG] Start userinfo request
2019-04-15 11:16:57.189 +00:00 [DBG] Bearer token found in header
2019-04-15 11:16:57.240 +00:00 [DBG] js found in database: true
2019-04-15 11:16:57.241 +00:00 [DBG] client configuration validation for client js succeeded.
2019-04-15 11:16:57.269 +00:00 [DBG] js found in database: true
2019-04-15 11:16:57.270 +00:00 [DBG] client configuration validation for client js succeeded.
2019-04-15 11:16:57.281 +00:00 [DBG] Calling into custom token validator: IdentityServer4.Validation.DefaultCustomTokenValidator
2019-04-15 11:16:57.283 +00:00 [DBG] Token validation success
leobelizquierdo
  • 1,648
  • 12
  • 20
  • are both the app and identity running over https ? – Fateh Mohamed Apr 12 '19 at 19:06
  • @FatehMohamed, yes both are running on https on azure. This is the only warning I see on console: `ResponseValidator._processSigninParams: Response was error login_required` – leobelizquierdo Apr 12 '19 at 19:06
  • i think it's related sameSite cookie attribute verification on prod, try to add: services.AddSession(options => options.Cookie.SameSite = SameSiteMode.None) in config services – Fateh Mohamed Apr 12 '19 at 19:21
  • @FatehMohamed I already test it, but I got the same result. I need to remove anything or just add `services.AddSession(options => options.Cookie.SameSite = SameSiteMode.None)`? – leobelizquierdo Apr 12 '19 at 19:39
  • if you ignore cookie policy, does the error remain? – Fateh Mohamed Apr 12 '19 at 19:45
  • what do you mean with ignore cookie policy? I don't have any error, just an infinite loop between login and logout – leobelizquierdo Apr 12 '19 at 19:47
  • do you really need `services.ConfigureApplicationCookie()`? When omit, IdSrv defaults it to "idsrv" + sets some additional options, such as `options.Cookie.SameSite = SameSiteMode.None;` You can try to do it yourself, but why? – d_f Apr 15 '19 at 16:05
  • see src/IdentityServer4/src/Configuration/DependencyInjection/ConfigureInternalCookieOptions.cs for the reference – d_f Apr 15 '19 at 16:12
  • @d_f, I get it, I remove it from my configurations, but still not working on deploy, can you explain me why only work locally? – leobelizquierdo Apr 15 '19 at 17:24
  • do you have any differences in your server structure on deploy to local ? – jonathan Heindl Apr 15 '19 at 21:12
  • @jonathanHeindl what do you mean? the code is the same, just change DB connection string, but I test with production db locally and works, so there isn't the db I think – leobelizquierdo Apr 15 '19 at 21:18
  • since youre having some CORS log entries up there I was wondering if for example you have the frontend running on a different url on the production systems compared to having them proxied to the same origin on your testing environment (usually the other way round is more likely but who knwos ^^) – jonathan Heindl Apr 15 '19 at 21:20
  • my production environment point to my production IS and so on. I think the problem is with the cookies not getting passed on production – leobelizquierdo Apr 15 '19 at 21:23
  • well cookies are set per origin so if your systems run on a different origin they wont get the cookies – jonathan Heindl Apr 15 '19 at 21:26
  • sorry I don't understand, what do you mean with different origin? You mean different domains? – leobelizquierdo Apr 15 '19 at 21:36
  • Yes (i usually say origin because they get differentiated by the location.origin string which contains scheme domain and port – jonathan Heindl Apr 16 '19 at 06:27
  • @leobelizquierdo have you tested with different browsers? – d_f Apr 16 '19 at 09:12
  • two more questionable registrations: `services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies", opts => { opts.SlidingExpiration = true; opts.ExpireTimeSpan = TimeSpan.FromMinutes(15); });` IdSrv registers the defaults itself. have you added your custom code intentionally? – d_f Apr 16 '19 at 09:15
  • yes, I was trying to create an expiration policy based on the cookie. I can reproduce the problem right now locally just changing the domain running the server, so how can I "pass" the cookie to the client if they have different domains? – leobelizquierdo Apr 16 '19 at 13:06
  • you should not. the point is that idsrv creates a cookie for its domain when you login. and when the client requires silent renew, the cookies still come to idsrv. the purpose of *SameSiteMode.None* setting is to allow the client attaching the cookie issued by IS to the request to IS. When you open (any) your idsrv page and inspect the cookies in dev console, you have to see *idsrv* and *idsrv.session* without any signs in *SameSite* column. – d_f Apr 16 '19 at 16:34
  • ok, right know I comment all related to cookies on IS and leave it as default. When I go to the browser console I can see the `idsrv.session` cookie with `SameSite` in blank as you sai (no `idsrv` cookie is present), but on the client cookies I don't get the `idsrv.session` cookie. This happnes if I set a custom server domain and run the client on localhost, if both are running on localhost I get the cookie on both apps. I don't know why the client doesn't get the cookie – leobelizquierdo Apr 16 '19 at 16:40
  • *idsrv* is the auth cookie. it should be there when you are logged in (with default configuration). you should see it in console when a page from that domain is open (for instance https://your.domain/.well-known/openid-configuration). there should not be *lax* nor *strict* restriction – d_f Apr 16 '19 at 17:02
  • Should you help me ? https://stackoverflow.com/questions/55761896 – Mirusky Apr 19 '19 at 20:14

1 Answers1

1

I finally find where to set the SameSite.None for the cookie in order to allow the browser send it back to IS when checking for session state. In my case I'm using ASP.NET Core Identity so the code is:

builder.Services.ConfigureApplicationCookie(options =>
{    
    options.Cookie.IsEssential = true;           
    // we need to disable to allow iframe for authorize requests
    options.Cookie.SameSite = AspNetCore.Http.SameSiteMode.None;
});

after:

var builder = services.AddIdentityServer(options =>
...
leobelizquierdo
  • 1,648
  • 12
  • 20
  • 1
    Great that you finally fixed it. But that's the default behavior for IdSrv, and I pointed to that on Apr 15 at 16:05 and 16:12, but you answered that you removed your custom override. So you could got it working a week earlier. – d_f Apr 22 '19 at 10:49
  • 1
    This answer certainly worked for me. Thank you, much appreciated. I don't know why the negative votes for this question. It immediately made me realize that this is the problem I'm currently facing. – Jukka Puranen Sep 20 '21 at 20:47