Why is IdentityServer redirecting to http rather than https? - identityserver3

I have a very simple MVC5 website that I'm trying to secure with IdentityServer3.
Both my website and my IdentityServer instance are hosted as separate sites in AppHarbor. Both are behind https.
When I hit a resource in my website that is protected by an [Authorize] attribute (e.g., /Home/About), I am successfully redirected to IdentityServer, and I can successfully authenticate.
When IdentityServer POSTs its response back to the website (via app.FormPostResponse.js), the website responds with a 302 redirect to the requested resource - as expected. However, this redirect is to http, not https (see the network trace below).
I'm sure this is just something wrong with my IdentityServer config, but I'd appreciate any pointers as to what I've got wrong.
(AppHarbor uses a reverse proxy (nginx I believe) in front of IIS, where SSL terminates - so I have RequireSsl = false for this scenario, as per the IdentityServer documentation.)
Here is my website's Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://<my-idsrv3>.apphb.com/identity",
ClientId = "<my-client-id>",
Scope = "openid profile roles email",
RedirectUri = "https://<my-website>.apphb.com",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false
});
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
}
}
Here is Startup.cs from my IdentityServer3 instance:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/identity", idsrvApp =>
{
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "My Identity Server",
SigningCertificate = Certificates.LoadSigningCertificate(),
RequireSsl = false,
PublicOrigin = "https://<my-idsrv3>.apphb.com",
Factory = new IdentityServerServiceFactory()
.UseInMemoryUsers(Users.Get())
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get())
});
});
}
}
Here is the definition of my website Client:
new Client
{
Enabled = true,
ClientName = "My Website Client",
ClientId = "<my-client-id>",
Flow = Flows.Implicit,
RedirectUris = new List<string>
{
"https://<my-website>.apphb.com"
},
AllowAccessToAllScopes = true
}
Here is the trace from Chrome, after clicking 'Yes, Allow' on the IdentityServer consent screen:

So it looks like this issue was caused by my client website being behind an SSL-terminating nginx front-end.
With reference to this GitHub issue, I added the following to the start of my website's app configuration:
app.Use(async (ctx, next) =>
{
string proto = ctx.Request.Headers.Get("X-Forwarded-Proto");
if (!string.IsNullOrEmpty(proto))
{
ctx.Request.Scheme = proto;
}
await next();
});
This makes the website aware that incoming requests were over https; this in turn appears to ensure that the IdentityServer3 middleware generates https uri's.

Had the same issue running identityserver4 in an Azure App Service. Even with forced https, the generated urls in .well-known/openid-configuration were still http://.
Fixed using the same solution as the other answer, but using AspNetCore ForwardedHeadersExtensions:
var forwardOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
// Needed because of mixing http and https.
RequireHeaderSymmetry = false,
};
// Accept X-Forwarded-* headers from all sources.
forwardOptions.KnownNetworks.Clear();
forwardOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardOptions);
See also https://github.com/IdentityServer/IdentityServer4/issues/1331 for more discussion on this subject.

Add forwarded headers in your startup
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
});
and
app.UseForwardedHeaders(new ForwardedHeadersOptions()
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
Finally tell the config it has to replace the http to https in the redirect url. I'm still looking for a better way to implement this.
in your .addopenidconnect() add:
Func<RedirectContext, Task> redirectToIdentityProvider = (ctx) =>
{
if (!ctx.ProtocolMessage.RedirectUri.StartsWith("https") && !ctx.ProtocolMessage.RedirectUri.Contains("localhost"))
ctx.ProtocolMessage.RedirectUri = ctx.ProtocolMessage.RedirectUri.Replace("http", "https");
return Task.FromResult(0);
};
opt.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = redirectToIdentityProvider
};

Related

What is default Javascript SignalR Client AuthenticationSchemes?

I have two clients for my SignalR app: Javascript client for web browsers and Android client (Gurgen/SignalR-.net-core-android-client) that use JWT Bearer Authentication.
I added this attribute to my SignalR Hub:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme + "," + CookieAuthenticationDefaults.AuthenticationScheme)]
public class MyHub : Hub
And my Startup.cs file:
public void ConfigureServices(IServiceCollection services)
{
//…
services.AddAuthentication()
.AddCookie(cfg => cfg.SlidingExpiration = true)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
};
});
services.AddSignalR();
//…
Android client login successfully with header authentication bearer token. But web client comes failed connecting to the hub (401 Unauthorized).
When I remove [Authorize] attribute the javascript client works!
What is default Javascript SignalR Client AuthenticationScheme? Or what is the issue I made?
I use dotnet core 2.1, Microsoft.AspNetCore.SignalR and my IDE is Visual Studio for Mac.
This is from SignalR docs:
In standard web APIs, bearer tokens are sent in an HTTP header. However, SignalR is unable to set these headers in browsers when using some transports. When using WebSockets and Server-Sent Events, the token is transmitted as a query string parameter.
services.AddAuthentication(options =>
{
// ...
})
.AddJwtBearer(options =>
{
// ...
// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or
// Server-Sent Events request comes in.
// Sending the access token in the query string is required due to
// a limitation in Browser APIs. We restrict it to only calls to the
// SignalR hub in this code.
// See https://learn.microsoft.com/aspnet/core/signalr/security#access-token-logging
// for more information about security considerations when using
// the query string to transmit the access token.
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});

Identity Server 3 + ASP.NET Core 2.0 MVC app - Federated single sign-out not including a redirect to ADFS before ending session

My web app is client to an Identity Server 3 STS, which is federated with ADFS for the external IdP. Sign-in works great. Sign-out from the STS is fine. But I have never been able to get IdSrv3 to redirect to ADFS for sign-out prior to ending the IdSrv3 session and ultimately redirecting to the app.
If I understand correctly, I should be able to have ADFS post back to the RP (IdSrv3) after signing out, at which point IdSrv3
Read the docs:
https://identityserver.github.io/Documentation/docsv2/advanced/federated-post-logout-redirect.html
As well as much of the anthology of the GitHub issues surrounding this topic of federated single sign-out.
Tracing through IdSrv3 I never see an attempt to redirect to ADFS for sign-out, so I assume I'm missing configuration here.
Once complexity is that I'm running IdSrv3 however my client apps are ASP.NET Core 2.0 so many of the samples don't cleanly reconcile with the latest Microsoft identity client middleware.
On the IdSrv3, these are (I believe) the relevant configuration components:
Configuration of Additional Identity Providers:
var wsFed = new WsFederationAuthenticationOptions
{
Wtrealm = ConfigurationManager.AppSettings["Wtrealm"],
MetadataAddress = metaDataAddress,
AuthenticationType = "ADFS",
Caption = "ACME ADFS",
SignInAsAuthenticationType = signInAsType
};
The IdSrv3 middleware:
coreApp.UseIdentityServer(
new IdentityServerOptions
{
SiteName = "eFactoryPro Identity Server",
SigningCertificate = Cert.Load(),
Factory = factory,
RequireSsl = true,
AuthenticationOptions = new AuthenticationOptions
{
IdentityProviders = ConfigureAdditionalIdentityProviders,
EnablePostSignOutAutoRedirect = true,
EnableSignOutPrompt = false,
EnableAutoCallbackForFederatedSignout = true
},
LoggingOptions = new LoggingOptions
{
EnableHttpLogging = true,
EnableKatanaLogging = true,
//EnableWebApiDiagnostics = true,
//WebApiDiagnosticsIsVerbose = true
}
});
coreApp.Map("/signoutcallback", cleanup =>
{
cleanup.Run(async ctx =>
{
var state = ctx.Request.Cookies["state"];
await ctx.Environment.RenderLoggedOutViewAsync(state);
});
});
});
Now for the Client side, an ASP.NET Core 2.0 MVC application:
Update: See accepted answer - the redirect to IdP for sign-out should have been handled on the IdSrv3 side with respect to redirecting to the external IdP (ADFS)
public static void ConfigureAuth(this IServiceCollection services,
ITicketStore distributedStore,
Options.AuthenticationOptions authOptions)
{
services.AddDataProtection();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromHours(8);
options.SlidingExpiration = true;
options.SessionStore = distributedStore;
})
.AddOpenIdConnect(options =>
{
options.Authority = authOptions.Authority;
options.ClientId = authOptions.ClientId;
options.ClientSecret = authOptions.ClientSecret;
options.ResponseType = "code id_token";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("roles");
options.Scope.Add("email");
options.Scope.Add("offline_access");
options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProviderForSignOut = n =>
{
var idTokenHint = n.ProtocolMessage.IdTokenHint;
if (!string.IsNullOrEmpty(idTokenHint))
{
var sessionId = n.HttpContext?.Session?.Id;
var signOutRedirectUrl = n.ProtocolMessage.BuildRedirectUrl();
if (sessionId != null)
{
n.HttpContext.Response.Cookies.Append("state", sessionId);
}
n.HttpContext?.Session?.Clear();
n.Response.Redirect(signOutRedirectUrl);
}
return Task.FromResult(0);
}
};
});
}
From the documentation I should be passing the "sign out message id" into that 'state' cookie. However, this extension method doesn't work in ASP.NET Core 2.0 as we don't really have access to OwinContext anymore.
var signOutMessageId = n.OwinContext.Environment.GetSignOutMessageId();
I've even tried instantiating a new OwinContext(n.HttpContext) to get at the environment dictionary - however, the value that the "GetSignOutMessageId()" obtains has a key of "id" which I can't find in the Owin variables.
It seems this cookie is really just necessary to persist state through all of the redirects so that after the PostLogoutUri of my client application is hit, which is currently set to "https://myapp/signout-callback-oidc", the message id can be used to finish cleaning up the session.
I'm also confused as to what role the "EnableAutoCallbackForFederatedSignout = true" setting plays on the IdSrv3 configuration.
From this description and looking at the code it would apear that this just saves me from having to set the "WReply" parameters on the ADFS signout:
https://github.com/IdentityServer/IdentityServer3/issues/2613
I would expect that ADFS would redirect to:
"https://myIdSrv3/core/signoutcallback" automatically if this settings was 'true'.
If anyone has any guidance to share it is much appreciated.
It turns out I was conflating some of the concepts in IdSrv3 that describe Federated Single Sign-Out initiated by the External Idp as opposed to my use case - sign-out initiated by the IdSrv3 client app, cascading "up" to the external IdP.
The root cause of this problem was in my UserService implementation. There I had overriden the "AuthenticateExternalAsync()" method, but did not specify the external identity provider in the AuthenticateResult object.
Here is the corrected implementation:
public override Task AuthenticateExternalAsync(ExternalAuthenticationContext context)
{
...
context.AuthenticateResult = new AuthenticateResult(
user.Id,
user.UserName,
new List<Claim>(),
context.ExternalIdentity.Provider);
return Task.FromResult(0);
}
Once the External Idp was specified in my AuthenticateResult, I was able to handle the WsFederationAuthenticationNotifications.RedirectToIdentityProvider event.
For the sake of completeness, here is my code to handle federated sign-out (client intiatited) from ADFS vis WsFed. It is more or less straight from the IdSrv3 documentation:
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.IsSignOutMessage)
{
var signOutMessageId = n.OwinContext.Environment.GetSignOutMessageId();
if (signOutMessageId != null)
{
n.OwinContext.Response.Cookies.Append("state", signOutMessageId);
}
var cleanUpUri =
$#"{n.Request.Scheme}://{n.Request.Host}{n.Request.PathBase}/external-signout-cleanup";
n.ProtocolMessage.Wreply = cleanUpUri;
}
return Task.FromResult(0);
}
}
And finally, my /external-signout-cleanup implementation:
coreApp.Map("/external-signout-cleanup", cleanup =>
{
cleanup.Run(async ctx =>
{
var state = ctx.Request.Cookies["state"];
await ctx.Environment.RenderLoggedOutViewAsync(state);
});
});

The audience is invalid error

I have 3 projects 1- Javascript SPA 2- Web API Project, 3- IdentityServer with EF Core
I started debugging API and Identity Server and successfully get the jwt token but, when I try to get value from API method which has Authorize Attribute I get an error:
WWW-Authenticate →Bearer error="invalid_token", error_description="The audience is invalid"
I could not found any property about audience in auth options. This is my configuration in API project
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
ApiSecret="secret",
Authority = "http://localhost:5000",
ApiName="fso.Api",
RequireHttpsMetadata = false,
});
And my Config.cs file in Identity
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource()
{
Name = "fso.Api",
DisplayName = "feasion API",
Scopes =
{
new Scope("api1"),
new Scope(StandardScopes.OfflineAccess)
},
UserClaims =
{
JwtClaimTypes.Subject,
JwtClaimTypes.EmailVerified,
JwtClaimTypes.Email,
JwtClaimTypes.Name,
JwtClaimTypes.FamilyName,
JwtClaimTypes.PhoneNumber,
JwtClaimTypes.PhoneNumberVerified,
JwtClaimTypes.PreferredUserName,
JwtClaimTypes.Profile,
JwtClaimTypes.Picture,
JwtClaimTypes.Locale,
JwtClaimTypes.IdentityProvider,
JwtClaimTypes.BirthDate,
JwtClaimTypes.AuthenticationTime
}
}
};
}
public static List<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Email(),
new IdentityResources.Profile(),
};
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "fso.api",
AllowOfflineAccess=true,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes =
{
StandardScopes.OfflineAccess,
"api1"
}
}
};
}
}
See here for what this claim is about:
The aud (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the aud claim when this claim is present, then the JWT MUST be rejected....
So your API's name must exist in the aud claim for the JWT to be valid when it is validated by the middleware in your API. You can use jwt.io to look at your token by the way, that can be useful to help make sense of it.
In order to have IdentityServer to add your API's name to the aud claim your client code (which is attempting to get a resource from the API and therefore needs an access token) should request a scope from your API. For example like this (from an MVC client):
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Authority = Configuration["IdpAuthorityAddress"],
ClientId = "my_web_ui_id",
Scope = { "api1" },
//other properties removed...
});
To avoid the error, audience should be consistently added in 4 places
In My (e.g. MVC) client as custom Scope.
In API application as ApiName
In IdentityServer Clients configuration as AllowedScope
In API Resources configuration as ApiResource
See details ( previously available in IdentityServer4 wiki):
When configuring a new API connection in identityServer4, you can get an error:
WWW-Authenticate: Bearer error="invalid_token",
error_description="The audience is invalid"
To avoid the error, Audience should be consistently added in 4 places
In My (e.g. MVC) client as custom Scope :
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Authority = Configuration["IdpAuthorityAddress"],
ClientId = "my_web_ui_id",
Scope = { "openid", "profile", "offline_access", "MyApi" },
//other properties removed for brevity...
});
In API application as ApiName
//Microsoft.AspNetCore.Builder.IdentityServerAuthenticationOptions
var identityServerAuthenticationOptions = new IdentityServerAuthenticationOptions()
{
Authority = Configuration["Authentication:IdentityServer:Authority"],
RequireHttpsMetadata = false,
EnableCaching = false,
ApiName = "MyApi",
ApiSecret = "MyApiSecret"
};
In IdentityServer \IdentityServerHost\Configuration\Clients.cs
(or corresponding Clients entry in the database)
var client = new Client
{
ClientId = clientId,
//other properties removed for brevity...
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
//IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess, "MyApi",
},
};
In IdentityServer \IdentityServerHost\Configuration\Resources.cs (or corresponding ApiResource entry in the database) as apiResource.Scopes
var apiResource = new ApiResource
{
Name = "MyApi",
ApiSecrets =
{
new Secret("MyApiSecret".Sha256())
},
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Profile,
},
};
In your app configuration file in AD configuration section add "Audience" line:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "<-- Enter the Client Id -->",
"Audience": "<-- Enter the Client Id -->",
"TenantId": "<-- Enter the tenantId here -->"
}
In my case "ClientId" & "Audience" was the same.
P.S.: And if after that you'll see
IDW10201: Neither scope or roles claim was found in the bearer token
Add another line to AD configuration:
"AllowWebApiToBeAuthorizedByACL": true
More here
In IdentityServer had to add claim "aud" to the jwt Token. In Order to do that under .AddJwtBearer("Bearer", options => options.Audience="invoice" and set ApiResource
Reference Link https://identityserver4.readthedocs.io/en/latest/topics/resources.html#refresources
public static readonly IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("invoice", "Invoice API")
{
Scopes = { "invoice.read", "invoice.pay", "manage" }
}
};
}

ASP.NET Core Authorize Redirection to wrong URL

I am trying to run a web application with the following route mapped:
app.UseMvc(routes =>
{
routes.MapRoute(
"default",
"WoL/{controller=Account}/{action=Login}/{id?}");
});
If the user is not authenticated and tries to access a action having the AuthorizeAttribute, the user should be redirected to the default login URL (as seen above). But the user gets redirected to "/Account/Login" instead of "/WoL/Account/Login". How can I redirect the user to "/WoL/Account/Login", if the user is not authenticated? I have configured the following Cookie Authentication:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
LoginPath = new PathString("/WoL/Account/Login"),
AutomaticChallenge = true
});
The answer of #Dmitry is not working anymore in ASP.NET Core 3.1. Based on the documentation that you can find here, you have to add the following code to the ConfigureServices:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
});
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = $"/Identity/Account/Login";
options.LogoutPath = $"/Identity/Account/Logout";
options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});
This works for me (in Startup.ConfigureServices):
services.AddIdentity<User, UserRole>(options =>
{
options.Cookies.ApplicationCookie.LoginPath = new PathString("/Admin/Account/Login");
});
Try to add options.ForwardChallenge = "oidc"; to AddCookie options config

ASP.NET MVC5 OWIN: Why User.Identity.IsAuthenticated == false after signing in via Facebook?

I am following the example of the VS2013 SPA Template - however am not using Bearer Tokens (this maybe the problem but would like to get this working just with cookies if possible).
Here is a cut down version of the relevant action method in my API Controller:
[HttpGet]
[AllowAnonymous]
public async Task<IHttpActionResult> ExternalLogin(string provider, string error = null)
{
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
// ...do stuff with externalLogin data
}
Here is the basic flow of requests:
Users clicks Facebook button, sends GET to /api/externallogin?provider=Facebook
User.Identity.IsAuthenticated returns false -> results in 401 being returned and middleware converts that into a 302 with the 'Location' header set to the facebook login page
Browser goes to the facebook login page
(https://www.facebook.com/dialog/oauth?response_type=code&client_id={myClientId}&redirect_uri=http%3A%2F%2Flocalhost%3A54819%2Fsignin-facebook&scope=user_birthday&state={someStateCode})
User signs in via Facebook -> results in browser making a call to the 'redirect_uri' but now with 'code' and 'state' parameters in the query string i.e. http://localhost:54819/signin-facebook?code={someCode}&state={someStateCode}
Response from the call to the 'redirect_uri' is a 302 with Location header set back to my application and also contains two 'Set-Cookie' headers:
Location: http://localhost:54819/api/en-gb/account/externallogin?provider=Facebook
Set-Cookie: .AspNet.Correlation.Facebook=; expires=Thu, 01-Jan-1970 00:00:00 GMT
Set-Cookie: .AspNet.ExternalCookie=Rv01CHd2onYtN_MHw2Bt71JSaOP71uRk7AP6kSilnAg7djXMh5fbZxlRCPuZhy8inhEF7ChNB261WVU3LGDuIaQmMXgz7tqXeNI-ji8qQFi2d64a720PbRVpWnkuHm2m8L87fkJAGQMJOku5gMrc0EZJfKNgjXiLv-c6Vo7PEzNch-CqcCPFHP0KBo7tGhDTbgJt-RvTzkkB1NL2JBc23eiaeda70oAW4P0NfIyj_i9mLUexHXz8Qooy9CBoLrN7Z198H_cawBfiMMF0tK1YFee2eH_TQxdmdKkUFVRz58EeIKyKUEEDswbQA9evPEHpD8BIlJPXi6R2scC44_INufXuKjHOt7LW3-sPRkUGbEWCWOn4d1B4FkHR_xOHtRpGpIdZU14xJLLiyFYKR0XxJiRlRIph8KKYnZHy61wMOl2yznOFqq3rzHOGhZ1xXEKmUlByiawPbNpdS9pNZVSHlGMbiz0FsOTf4_EVAKEXRQyxEbYjBBXD_5Ne6f7SpBqE; path=/; HttpOnly
Browser then sends GET request to the URL in the Location header from step 5 (back to my application), with the following cookie (as per the above 'Set-Cookie' directive):
Cookie: .AspNet.ExternalCookie=Rv01CHd2onYtN_MHw2Bt71JSaOP71uRk7AP6kSilnAg7djXMh5fbZxlRCPuZhy8inhEF7ChNB261WVU3LGDuIaQmMXgz7tqXeNI-ji8qQFi2d64a720PbRVpWnkuHm2m8L87fkJAGQMJOku5gMrc0EZJfKNgjXiLv-c6Vo7PEzNch-CqcCPFHP0KBo7tGhDTbgJt-RvTzkkB1NL2JBc23eiaeda70oAW4P0NfIyj_i9mLUexHXz8Qooy9CBoLrN7Z198H_cawBfiMMF0tK1YFee2eH_TQxdmdKkUFVRz58EeIKyKUEEDswbQA9evPEHpD8BIlJPXi6R2scC44_INufXuKjHOt7LW3-sPRkUGbEWCWOn4d1B4FkHR_xOHtRpGpIdZU14xJLLiyFYKR0XxJiRlRIph8KKYnZHy61wMOl2yznOFqq3rzHOGhZ1xXEKmUlByiawPbNpdS9pNZVSHlGMbiz0FsOTf4_EVAKEXRQyxEbYjBBXD_5Ne6f7SpBqE
THE PROBLEM: User.Identity.IsAuthenticated check returns False at this stage (in fact the User field is basically empty)
I would have thought, given that the AspNet.ExternalCookie is definitely being sent in the request at step 6 then the user is thereby Authenticated.
So, does anyone know what the middleware would be looking for at this stage in order for it to decode/decrypt/de-serialize the cookie and saturate the User???
Here is the Startup.Auth I have:
public void ConfigureAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
AuthenticationType = Constants.AuthenticationTypes.MchAdminApplicationCookie,
SlidingExpiration = true,
ExpireTimeSpan = new TimeSpan(10, 0, 0)
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
AuthenticationType = Constants.AuthenticationTypes.MchApiApplicationCookie,
SlidingExpiration = true,
ExpireTimeSpan = new TimeSpan(10, 0, 0)
});
var facebook = new FacebookAuthenticationOptions
{
AppId = "mycode",
AppSecret = "mysecret",
AuthenticationType = "Facebook",
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = async ctx =>
{
if (ctx.User["birthday"] != null)
{
ctx.Identity.AddClaim(new Claim(ClaimTypes.DateOfBirth, ctx.User["birthday"].ToString()));
}
}
}
};
facebook.Scope.Add("user_birthday");
app.UseFacebookAuthentication(facebook);
}
I encountered the same problem. You should add two attributes to ExternalLogin action:
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]