Correlation failed on Identity Server 4 - .net-core

I have an Identity Server 4 configured with OpenIdConnect to Azure AD.
When user clicks on login button, IS4 redirects to Azure AD and on callback to IS4, it shows this error:
This is how I request token from postman:
Note that callback url is mobile application format.
This is my configuration:
services.AddAuthentication()
.AddCookie(options => new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromHours(12),
SlidingExpiration = false,
Cookie = new CookieBuilder
{
Path = "",
Name = "MyCookie"
}
}).AddOpenIdConnect(options =>
{
options.ClientId = configuration["OpenIdConnect:ClientId"];
options.Authority = configuration["OpenIdConnect:Authority"];
options.SignedOutRedirectUri = configuration["OpenIdConnect:PostLogoutRedirectUri"];
options.CallbackPath = configuration["OpenIdConnect:CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.Resource = configuration["OpenIdConnect:Resource"];
options.ClientSecret = configuration["OpenIdConnect:ClientSecret"];
options.SaveTokens = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
});
And this are my parameters:
"OpenIdConnect": {
"ClientId": "xxxxxxxxxx",
"Authority": "https://login.microsoftonline.com/xxxxxxxxxx/",
"PostLogoutRedirectUri": "https://uri-of-my-identity-server.azurewebsites.net",
"CallbackPath": "/signin-oidc",
"ResponseType": "code id_token",
"Resource": "https://graph.microsoft.com/",
"ClientSecret": "my-secret"
},
Note: this error only occurs on Azure environment (not locally)
Note: on Xamarin application, when Azure returns to IS4 consent screen, it shows this message:

It could be that there is an issue with the networking between your client and Azure. A certain port has not been opened or a load balancer is in between.
When decryption fails, state is null, thus resulting in a Correlation failed: state not found error. In our case, decryption failed because different keys were used for encryption/decryption, a pretty common problem when deploying behind a load balancer.

Related

Automatically Attaching Identity Cookie to HTTP Client in Blazor wasm

I am working on a blazor application where I used my API project as Identity
Provider. Everything is working fine but the issue is that the access token
issued by my API is not validated by the API. It turns out the API is expecting a
cookie header. I took a closer look at blazor hosted application and found out
the cookie is being sent along with each request but it's same-origin.
My Blazor WASM project does not automatically attach this cookie in the request
header, just the access token.
Is there a way I can make the Http handler attach this cookie on each request?
or make the API validate the access token instead of the identity cookie.
This is my startup class in the API Project
public static void AddIdentityServer(IServiceCollection services,IConfiguration configuration)
{
services.AddIdentityServer(options =>
{
options.UserInteraction.LoginUrl = "/Identity/Account/Login";
options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
}).AddProfileService<LocalProfileService>()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(option =>
{
option.Clients.Add(new Client
{
ClientId = "blazor",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
AllowedCorsOrigins = { "https://localhost:5001" },
AllowedScopes = { "openid", "profile", "email","id" },
RedirectUris = { "https://localhost:5001/authentication/login-callback" },
PostLogoutRedirectUris = { "https://localhost:5001/" },
Enabled = true,
RequireConsent = false,
});
option.IdentityResources.AddEmail();
option.IdentityResources["openid"].UserClaims.Add("name");
option.ApiResources.Single().UserClaims.Add("name");
option.IdentityResources["openid"].UserClaims.Add("role");
option.ApiResources.Single().UserClaims.Add("role");
option.IdentityResources.Add(new IdentityResource("id",new string[] {"id" }));
option.ApiResources.Single().UserClaims.Add("id");
});
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.ClientId = configuration["ExternalLoginApiKey:GoogleClientId"];
options.ClientSecret = configuration["ExternalLoginApiKey:GoogleClientSecret"];
})
.AddFacebook("Facebook", options =>
{
options.AppId = configuration["ExternalLoginApiKey:FacebookAppId"];
options.AppSecret = configuration["ExternalLoginApiKey:FacebookAppSecret"];
})
.AddIdentityServerJwt();
}
Program class in the Blazor Project
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("oidc", options.ProviderOptions);
options.UserOptions.RoleClaim = "role";
}).AddAccountClaimsPrincipalFactory<CustomUserFactory>();
builder.Services.AddHttpClient<IAuthorizedRestService, AuthorizedRestService>(
client => client.BaseAddress = new Uri("https://localhost:5002/api/mart/v1/"))
.AddHttpMessageHandler(sp => sp.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(authorizedUrls: new[] { "https://localhost:5002" }));
builder.Services.AddHttpClient("noauth", option => option.BaseAddress = new
Uri("https://localhost:5002/api/mart/v1/"));
builder.Services.AddScoped<IRestService, RestService>();
await builder.Build().RunAsync();
}
I have found the Solution.
It happens that there is already a JWT handler provided by IdentityServer4 for APIs that double as Authorization Server
.AddIdentityServerJwt();
So what I did was to configure it
services.Configure<JwtBearerOptions>
(IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "https://localhost:5002";
options.Audience = "mart";
options.SaveToken = true;
});
Then specify the Authentication scheme to use
[Authorize(AuthenticationSchemes = IdentityServerJwtConstants.IdentityServerJwtBearerScheme)]
You can also add it globally in the start up class
var authorizationPolicy = new AuthorizationPolicyBuilder(IdentityServerJwtConstants.IdentityServerJwtBearerScheme)
.RequireAuthenticatedUser().Build();
options.Filters.Add(new AuthorizeFilter(authorizationPolicy));
You can read more using these links
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-3.1
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.1

Authentication with MVC Client 4.7.1 and IdentityServer4

I am trying to integrate user authentication between an MVC 4.7.1 client and an ASP.NET Core 3.1 IdentityServer4 & ASP.NET Identity service.
I have been following this tutorial for cookie issued authentication: Refreshing your Legacy ASP.NET IdentityServer Client Applications (with PKCE)
So far, the MVC client is able to redirect to the Login page. Upon logging in, I have a null error in the following function:
private string RetrieveCodeVerifier(AuthorizationCodeReceivedNotification n)
{
string key = GetCodeVerifierKey(n.ProtocolMessage.State);
string codeVerifierCookie = n.Options.CookieManager.GetRequestCookie(n.OwinContext, key);
if (codeVerifierCookie != null)
{
var cookieOptions = new CookieOptions
{
SameSite = SameSiteMode.None,
HttpOnly = true,
Secure = n.Request.IsSecure
};
n.Options.CookieManager.DeleteCookie(n.OwinContext, key, cookieOptions);
}
string codeVerifier;
var cookieProperties = n.Options.StateDataFormat.Unprotect(Encoding.UTF8.GetString(Convert.FromBase64String(codeVerifierCookie)));
cookieProperties.Dictionary.TryGetValue("cv", out codeVerifier);
return codeVerifier;
}
Apparently the codeVerifierCookie is null.
The rest of the configuration is as follows.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "cookie"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "mvc.owin",
Authority = "https://localhost:44355",
RedirectUri = "http://localhost:5001/Auth/",
Scope = "openid profile scope1",
SignInAsAuthenticationType = "cookie",
RequireHttpsMetadata = false,
UseTokenLifetime = false,
RedeemCode = true,
SaveTokens = true,
ClientSecret = "secret",
ResponseType = "code",
ResponseMode = "query",
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
{
// set PKCE parameters
var codeVerifier = CryptoRandom.CreateUniqueId(32);
string codeChallenge;
using (var sha256 = SHA256.Create())
{
var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
codeChallenge = Base64Url.Encode(challengeBytes);
}
n.ProtocolMessage.SetParameter("code_challenge", codeChallenge);
n.ProtocolMessage.SetParameter("code_challenge_method", "S256");
// remember code_verifier (adapted from OWIN nonce cookie)
RememberCodeVerifier(n, codeVerifier);
}
return Task.CompletedTask;
},
AuthorizationCodeReceived = n =>
{
// get code_verifier
var codeVerifier = RetrieveCodeVerifier(n);
// attach code_verifier
n.TokenEndpointRequest.SetParameter("code_verifier", codeVerifier);
return Task.CompletedTask;
}
}
});
And on IdentityServer4 ConfigureServices:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddAspNetIdentity<ApplicationUser>();
Finally, Client.cs configuration on IdentityServer4 side:
new Client
{
ClientId = "mvc.owin",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.Code,
ClientSecrets = {new Secret("secret".Sha256())},
RedirectUris = {"http://localhost:5001/Auth/"},
AllowedScopes = {"openid", "profile", "scope1"},
AllowPlainTextPkce = false,
RequirePkce = true,
RequireConsent = false,
// Token lifetimes
AuthorizationCodeLifetime = 60,
AccessTokenLifetime = 60,
IdentityTokenLifetime = 60
}
AccountController and the rest is pretty much the basic IdentityServer4.Template, specifically is4aspid.
Anyone tried the same and knows what fails?
Is there a way to do it with JWT instead of Cookies? And, what are the drawbacks?
Edit: Apparently, this configuration works with Firefox, and I am suspecting this is a problem with Chrome's Same-Site cookie policy, hence the null in GetRequestCookie. The thing is, IdentityServer4 is running on HTTPS (otherwise, there are others) while the MVC client app is running on HTTP (note: both on localhost). I have tried using SameSite policy None, Lax, Strict and vise-versa with no success. I am not sure what else to try.
Best,
mkanakis.
Cookies that assert SameSite=None must also be marked as Secure, this means you need to use https
Read more here

Identityserver unauthorized_client error in implicit flow

My Identity Server works well in some weeks after that I have gotten an unauthorized_client error, I don't know why.
Identity Server host in http://localhost:5001
Angular Started with .Net Core project in http://localhost:4200
The exact error is:
Sorry, there was an error: unauthorized_client
Unknown client or client not enabled
In the Identity Server, my client defined as follow:
var clients = new List<Client>
{
new Client
{
ClientId = "app.spa.client",
ClientName = "Client Application",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris =
{
"http://localhost:4200/assets/oidc-login-redirect.html",
"http://localhost:4200/assets/silent-redirect.html"
},
PostLogoutRedirectUris = { "http://localhost:4200/?postLogout=true" },
AllowedCorsOrigins = new[] { "http://localhost:4200/" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"webapi"
},
IdentityTokenLifetime = 120,
AccessTokenLifetime = 120
}
};
And in Angular project, I'm using from oidc-client and my config is like follow:
var config = {
authority: "http://localhost:5001/",
client_id: "app.spa.client",
redirect_uri: `http://localhost:4200/assets/oidc-login-redirect.html`,
scope: "openid profile webapi",
response_type: "id_token token",
post_logout_redirect_uri: `http://localhost:4200/?postLogout=true`,
userStore: new WebStorageStateStore({ store: window.localStorage }),
automaticSilentRenew: true,
silent_redirect_uri: `http://localhost:4200/assets/silent-redirect.html`
};
Have you ever been this error?
How I can find more details of this error?
Actually, I found the problem, The IdentityServer4 package in Identity service updated from version 2.4.0 to 2.5.0 but I can't resolve this problem.
Eventually, I'm forced to be down-grade to 2.4.0 version and my problem solved.
Any Idea to solve this problem in IdentityServer4 version 2.5.0?

ASP.NET Boilerplate Identity Server API Access Token

I have successfully enabled Identity Server in ASP.Net Boilerplate and called it with a JS client as described by Identity Server docs http://docs.identityserver.io/en/latest/quickstarts/6_javascript_client.html . After logging on and getting an access token I cannot use this access token to access the API as I get 401.
I was unable to complete the final step as shown by Identity Server example. Because My projects throws an error on startup saying Identityserver already registered. I am assuming without this step my token is not generated correctly. However have not been able to work out how I should configure ABP to do the same thing.
services.AddAuthentication("Bearer").AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "api1";
});
Any help appreciated :)
The exact error I get is 401 (Unauthorized). If i exchange the token for one generated through /api/TokenAuth/Authenticate the call works.
The Token generated through Identity server is "eyJhbGciOiJSUzI1NiIsImtpZCI6IjNhODAzNTUzNzNlNDVhMzRmNTI3MzJmM2ZjZWNjZTQ4IiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NTExNTgyMjYsImV4cCI6MTU1MTE2MTgyNiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo2MjExNCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NjIxMTQvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoianMiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTUxMTU3MTcwLCJpZHAiOiJsb2NhbCIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiXSwiYW1yIjpbInB3ZCJdfQ.LBJ5KfiOGjMSWlpsWbXLuGBnd0RHq07IWM7npYGPOBm38ENeZkzLErgwalTFH7acOOa8rHymfTFRBVQgO1sEy-nnxn-iPmjstKABu2Xe1o-qlsrU7K7mxN1FLKJWksWBty983TZ-WLrK9pXEHjN9LGeBFY-Qx_RPFOVu4gattjgNI05-J3a2dsnON_bJfvsXPL2ktUa_od-uqi9AXnWY_kJA-5xh1rjMP6pf740tMQJjhMGAIitQHbWiCfmvvPjX6bzBnMXFJpmiVT_hZsZ76zoQskLRQz8Zn-IfVhU9VM-8U7B6PKUaVFs4-VA2ia9VVwxuSs1gJoC9RwMqKYmX_g"
My Client is being registered with these properties.
var client = new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
RequireConsent = true,
RedirectUris = { "http://localhost:5003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
AllowedCorsOrigins = { "http://localhost:5003" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"Apps"
}
};

Multiple Authentication Middlewares ASP.NET Core

I am relatively new to the concept of middlewares. I am aware that a middleware calls the next middleware when it completes.
I am trying to authenticate a request using either Google or my Identity Server. The user can login on my mobile app with google or a local account. However, I can't figure out how to use both authentication middlewares. If I pass the id_token for google, it passes on the first middleware (UseJwtBearerAuthentication) but fails on the second one (UseIdentityServerAuthentication). How can I make it so that it doesn't throw error when it actually passes on at least 1 authentication middleware? For example, if it passes on the first middleware, the second middleware is ignored?
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
Authority = "https://accounts.google.com",
Audience = "secret.apps.googleusercontent.com",
TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = true,
ValidIssuer = "accounts.google.com"
},
RequireHttpsMetadata = false
});
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:1000/",
RequireHttpsMetadata = false,
ScopeName = "MyApp.Api"
});
Normally, when an authentication middleware is failed(i don't mean throwing exception), this doesn't affect another successful authentication middleware. Probably your second middleware throws an exception(not a validation failure). First check error message and try to resolve it. If you can't, use AuthenticationFailed event to handle error. In this case your code should be something like below:
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
// ...
Events = new JwtBearerEvents()
{
OnAuthenticationFailed = async (context) =>
{
if (context.Exception is your exception)
{
context.SkipToNextMiddleware();
}
}
}
});
However, for your scenerio i wouldn't choose your way. I would use only identity server endpoint. For signing with google you can configure identity server like below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
AutomaticAuthenticate = false,
AutomaticChallenge = false
});
app.UseGoogleAuthentication(new GoogleOptions
{
AuthenticationScheme = "Google",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = "",
ClientSecret = ""
});
app.UseIdentityServer();
Edit
It seems AuthenticationFailed event couldn't be used for IdentityServer4.AccessTokenValidation. I am not sure but if you will use identity server for only jwt token, you can use UseJwtBearerAuthentication for validation.

Resources