IdentityModel / oidc-client-js

OpenID Connect (OIDC) and OAuth2 protocol support for browser-based JavaScript applications
Apache License 2.0
2.43k stars 842 forks source link

signinSilent returns login_required error in iframe #1281

Closed olee closed 3 years ago

olee commented 3 years ago

Hi, I have some problems with getting silent token renewal working.

To test it, I am calling signinSilent on the token client manually, which always results in the "login_required" error.

However, when I take the generated authorize url which the iframe loads with and insert it into a new tab, it redirects me to silent_renew.html with a proper token code and no error.

I already checked CSP (actually completely disabled them) and tried to search for the source of this issue everywhere, but I could not find any solution.

I am testing on localhost right now and the IdSrv runs on azure with the localhost url being a valid redirectUri

olee commented 3 years ago

Checking the logs turned up this information:

[Information] IdentityServer4.Hosting.IdentityServerMiddleware: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
[Information] IdentityServer4.Endpoints.AuthorizeEndpoint: ValidatedAuthorizeRequest
{
    "ClientId": "c3client_code",
    "RedirectUri": "http://localhost:65046/silent_renew.html",
    "AllowedRedirectUris": [
        "http://localhost:65046",
        "http://localhost:65046/silent_renew.html",
        <<snip>>
    ],
    "SubjectId": "anonymous",
    "ResponseType": "code",
    "ResponseMode": "query",
    "GrantType": "authorization_code",
    "RequestedScopes": "openid profile email client api",
    "State": "a909822290764035ac500e22f6dd48aa",
    "PromptMode": "none",
    "Raw": {
        "client_id": "c3client_code",
        "redirect_uri": "http://localhost:65046/silent_renew.html",
        "response_type": "code",
        "scope": "openid profile email client api",
        "prompt": "none",
        "response_mode": "query"
        <<snip>>
    }
}
[Information] IdentityServer4.ResponseHandling.AuthorizeInteractionResponseGenerator: Showing error: prompt=none was requested but user is not authenticated
[Information] IdentityServer4.Endpoints.AuthorizeEndpoint: 
{
    "ClientId": "c3client_code",
    "RedirectUri": "http://localhost:65046/silent_renew.html",
    "AllowedRedirectUris": [
        "http://localhost:65046",
        "http://localhost:65046/silent_renew.html",
        <<snip>>
    ],
    "SubjectId": "anonymous",
    "ResponseType": "code",
    "ResponseMode": "query",
    "GrantType": "authorization_code",
    "RequestedScopes": "openid profile email client api",
    "PromptMode": "none",
    "Raw": {
        "client_id": "c3client_code",
        "redirect_uri": "http://localhost:65046/silent_renew.html",
        "response_type": "code",
        "scope": "openid profile email client api",
        "prompt": "none",
        <<snip>>
    }
}

I looked into various similar issues, however none of the proposed solutions seem to work and as I said - opening the url directly in the browser instead of the iframe works.

I mean it's pretty obvious there's some really stupid mistake with my login cookie not being sent when loading in the iframe, however I cannot find the issue.

Here's my client config:

    new Client
    {
        Enabled = true,
        ClientName = "C3 Client code",
        ClientId = "c3client_code",
        ClientSecrets = new List<Secret> {
            new Secret("<snip>")
        },
        AllowedGrantTypes = GrantTypes.Code,
        AllowedScopes = AllowedScopes,
        AllowOfflineAccess = true,
        RequireConsent = false,
        RequirePkce = true,
        RequireClientSecret = false,
        AccessTokenType = AccessTokenType.Jwt,
        AccessTokenLifetime = TokenLifetime,
        RedirectUris = RedirectUris,
        PostLogoutRedirectUris = RedirectUris,
        AllowAccessTokensViaBrowser = true,
        AllowedCorsOrigins = CorsOrigins,
    },
olee commented 3 years ago

Ok I think this happens because auf same site policy setting and I'm trying to figure out right now how to properly set it.

brockallen commented 3 years ago

Are you using https?

olee commented 3 years ago

No I am not. However I noticed that my issue might be related to the identity server being hosted on Azure. It seems that for some reason the SameSite attribute cannot be set for any cookie.

I tried setting a test cookie manually with SameSite and when I checked it in the browser it was always returned without it.

Then I went on and added this little middleware in my application and now the silent renew is actually working correctly:

  app.Use(async (context, next) =>
  {
      await next();
      if (context.Response.Headers.TryGetValue("set-cookie", out Microsoft.Extensions.Primitives.StringValues values))
      {
          context.Response.Headers.Remove("set-cookie");
          foreach (var item in values)
          {
              context.Response.Headers.Append("set-cookie", 
                  item.StartsWith("idsrv") && !item.Contains("SameSite=") ? item + "; SameSite=None" : item
              );
          }
      }
  });

However, this looks like a horrible solution, so I still want to find out why this is not working.

brockallen commented 3 years ago

Oh yea, you might be dealing with the browser's ITP when the STS is on a different site. That's just not going to work.

https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/

olee commented 3 years ago

But I was able to fix it with the above code on server site, so wouldn't it be some kind of ASP / Azure / .NET issue?

EDIT: Also I just noticed that this happens only on our deployed dev environment, however on the productive one it works right now... though it also worked on dev in the past (before Chrome's cookie update)

EDIT 2: Checking again, I see that the login cookies are also sent on prod without SameSite attribute image However, the silent renew is working there because login is just running on another subdomain it seems.

brockallen commented 3 years ago

Yea, SPAs doing work cross site is going to be more and more difficult. You might need to use a different architecture:

https://leastprivilege.com/2019/01/18/an-alternative-way-to-secure-spas-with-asp-net-core-openid-connect-oauth-2-0-and-proxykit/

droid001 commented 3 years ago

I had a similar problem this morning with all browser types except Mozilla. Managed to get rid of it with placing monitorSession: false to the localdev config. Don't know if this will help with this issue though.

brockallen commented 3 years ago

I think we can close this now?

olee commented 3 years ago

Well my issue was not exactly solved - I just hacked a dirty workaround - but it was not really an issue caused by Identity Server, so it's fine to close it. Thanks for the help anyway and keep up the good work 👍

mellis481 commented 3 years ago

Are you using https?

@brockallen What if he was? I'm seeing this and I'm on https.

brockallen commented 3 years ago

If you don't use HTTPS then you can't set SameSite to None.