asp.net mvc login from identityserver4 with a invalid_request error - asp.net-mvc-4

today i use the demo of identityserver4 Build a validation server, and i can use the asp.net core client with openid login the client.
but i could not login my asp.net mvc5 client with openid, The error of the prompt is : invalid_request,
here is my identityserver4 config code with getclient()
// clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
// OpenID Connect hybrid flow and client credentials client (MVC)
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = true,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
};
}
}
and the follow code is my asp.net mvc5 clent ConfigureAuth(),because the idenetiyServer4 define the ClientSecrets is "secret".Sha256(),so in this mvc client , i set the ClientSecret = GetSHA256HashFromString("secret"),i create prvate the method GetSHA256HashFromString() to convert the string to sha256.
here is my code:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "Cookies",
Authority = "http://localhost:5000", //ID Server SSO Server
ClientId = "mvc",
ClientSecret = GetSHA256HashFromString("secret"),
ResponseType = "code id_token",
RedirectUri = "http://localhost:5002/signin-oidc", //URL of Client website
PostLogoutRedirectUri = "http://localhost:5002/signout-callback-oidc", //URL of Client website
Scope = "api1",
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
RequireHttpsMetadata = false,
});
and i press f5 to run the mvc client ,and press the button of login,the brower can jump to the localhost:5000,but it is give me a error:
Sorry, there was an error : invalid_request and the other error info are :
Request Id: 0HL9RHBTJIT3T:00000003**
thanks a lot.

The value of ClientSecret should be the actual secret value not the hashed one.
The secret is stored as hash when you use a persisted data storage to prevent an attacker to obtain your client's secrets in case if your storage is compromised.
In your case, The secret value is "secret". So the code will be
ClientSecret = "secret"

Related

IdentityServer4 ADFS External Not Returning Roles

I have created an IdentityServer4 IDP using the standard template for Core Identity. I am looking to have an External provider being out ADFS 2016 Server. I have added this to the AddAuthentication() in Startup.cs
services.AddAuthentication()
.AddOpenIdConnect("adfs", "ADFS", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "[AuthURL]";
options.ClientId = "[ClientId]";
options.ResponseType = "id_token token code";
options.Scope.Add("profile");
options.Scope.Add("email");
options.CallbackPath = "/signin-adfs";
options.SignedOutCallbackPath = "/signout-callback-adfs";
options.RemoteSignOutPath = "/signout-adfs";
options.ClaimActions.Add(new JsonKeyClaimAction("role", null, "role"));
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
It successfully redirects to the ADFS login window. Once signed in it calls the ExternalController.cs CallBack() correctly and I do have a successful authentication.
public async Task<IActionResult> Callback()
{
// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
The issue I am having is that I am not getting back the full user. I see claims but I do not see roles. I am used to seeing a JWT token which includes a list of Roles, however, I do not see these roles within the Result from above.
How can I either get a JWT token back from the Authentication against ADFS or have the roles returned and be within the Result?
Inside OIDC add scope for roles:
options.Scope.Add("roles");
Inside config.cs in IDS4 make sure your client has the allowed scopes:
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"roles",
},
If it's an API, make sure config.cs has the correct userclaims:
new ApiResource()
{
Name = "API",
DisplayName = "display",
ApiSecrets =
{
new Secret("secret".Sha256()),
new Secret
{
Value = "4A04D56554F731CCD123BB574D6918C8C83BDF65",
Type = "X509Thumbprint",
Description = "Certificate"
}
},
Scopes = new List<string>(){"postman Web API"},
Enabled = true,
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Email,
JwtClaimTypes.Subject,
JwtClaimTypes.Role,
JwtClaimTypes.Address,
JwtClaimTypes.Confirmation,
JwtClaimTypes.EmailVerified,
JwtClaimTypes.Id,
JwtClaimTypes.Profile
}
},

identityserver4 Hybrid follow without competed login

I have an API that needs to secure allowing 3rd party asp.net core MVC web application to request an access token and use this access token to request the secured API.
i created HybridAndClientCredentials client on the identity server
ClientId = "testclient",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AllowOfflineAccess = true,
// secret for authentication
ClientSecrets =
{
new Secret("password".Sha256())
},
RedirectUris = {"http://127.0.0.1:55950",
"http://localhost/testLogin",
"https://localhost:44322/",
"https://localhost:44302/",
"https://localhost:44303/signin-oidc"},
RequireConsent = false,
// scopes that client has access to
AllowedScopes = { "roles" , IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile}
and MVC client as showing in identity server 4 documentation
.AddOpenIdConnect("oidc", options =>
{
options.Authority = Constants.Authority;
options.RequireHttpsMetadata = false;
options.ClientSecret = "password";
options.ClientId = "testclient";
options.ResponseType = "code id_token";
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
//options.Scope.Add("email");
//options.Scope.Add("resource1.scope1");
options.Scope.Add("offline_access");
options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash");
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
};
});
it's working but the user appears to be logged in the MVC application all I want is the access token to be used in calling the secured API also I don't want to use [authorize] attribute on the MVC client to redirect the user to identity server login page.
Something in the MVC application must to trigger a challenge to the OpenIDconnect handler, that starts the process to authenticate the user. As a result the user is logged in and you get access to the tokens.
Using a [Authorize] attribute is one way to trigger a challenge, or to do it manually using code like:
public async Task Login()
{
await HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme,
new AuthenticationProperties() { RedirectUri = "/" });
}

Set AccessToken Validation on a .net wepapi 2 (not core) with Identity server 4

I know how to setup IdentityServer 4 Authentication in .Net core. That is: using the extensions defined in IdentityServer4.AccessTokenValidation. And I would set it up in my startup class like so:
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ApiName = "webapi"
});
The problem is that now I need to make authenticated requests to a .net 4.6 web api2 (not core). And the same package doesn't work for that.
According to this question all I have to do is to use the same package that was used for Identity server 3:IdentityServer3.AccessTokenValidation.
But After trying it out all I get is 401 when making requests to the web api. And I don't know how to wire authentication events to understand the reason behind it. Here is my configuration:
Api Startup.cs:
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "http://localhost:5000",
RequiredScopes = new[] { "webapi" },
});
Client Startup.cs:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "Cookies",
Authority = "http://localhost:5000",
RedirectUri = "http://localhost:3954/signin-oidc",
ClientId = "MvcClient",
Scope = "openid profile webapi offline_access",
ResponseType = "code id_token",
ClientSecret = "secret",
UseTokenLifetime = false,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
},
});
TestController in the Client project:
var tokenClient = new TokenClient("http://localhost:5000/connect/token", "MvcClient", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("webapi");
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var content = await client.GetStringAsync("http://localhost:5004/api/identity");
I successfully get an access token here. But get a 401 when making the request to api/identity.
Here is the Config in the IDP:
new ApiResource("webapi", "My API")
[...]
new Client
{
ClientId = "MvcClient",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = true,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:3954/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:3954/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"netcoremvcapi",
"webapi"
},
AllowOfflineAccess = true,
}
Any idea why this might be failing? Am I making the wrong assumption that I can use IdentityServer3.AccessTokenValidation to validate the token?
The first issue was that my startup class wasn't being called. That was solved by adding the Microsoft.Owin.Host.SystemWeb package.
Then it got interesting. I started getting an exception while registering OpenIdConnectAuthenticationMiddleware into the OWIN runtime:
Could not load type 'System.IdentityModel.Tokens.TokenValidationParameters' from assembly
'System.IdentityModel.Tokens.Jwt, Version=5.0.0.127
And the reason is because my web api 2 project is using the Owin implementation Katana. And apparently Katana does not support v5.0 of that package. As described here and here.
So for that I had to remove that package first (downgrade didn't work due to dependency). That also failed because of the dependency on my version of the package Microsoft.Owin.Security.Jwt. And that, of course, led to another issue with a dependency of another package....after removing all the dependant packages and reinstalling System.IdentityModel.Tokens.Jwt v4.0.2 and then IdentityServer3.AccessTokenValidation. It all worked with the same setup described above...

IdentityServer4 : Service to service call fails to find Audience

I am trying to make use of IdentityServer4 for authenticating the user for a Micoservices architecture. When I am trying to make a call from my Web App controller to another Web API service, the call fails on the Web API service with the message on the console that looks like below:
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException:
IDX10214: Audience validation failed.
Audiences: 'null/resources'. Did not match: validationParameters.ValidAudience: 'hierarchy' or validationParameters.ValidAudiences: 'null'.
My chain of call is something like below:
[User] --> [ASP.Net core MVC: Web App ]--> [ASP.Net core MVC: Web Api ]
I have created an Identity server using the IdentityServer4 with the following configuration:
API resources
new ApiResource("hierarchy", "Hierarchy Configuration API"),
new ApiResource("deviceconfiguration", "Device Configuration API"),
Client
new Client
{
ClientId = "system.health.check",
ClientName = "System health check client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"healthcheck"
},
RedirectUris = { "http://localhost:5100/signin-oidc", "http://localhost:5103/signin-oidc",},
PostLogoutRedirectUris = { "http://localhost:5100/signout-callback-oidc" },
}
Service - Startup
services.AddIdentityServer(x => x.IssuerUri = "null")
.AddSigningCredential(Certificate.Certificate.Get())
.AddTemporarySigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
[ASP.Net core MVC: Web App ]
I have setup my opthion as below:
"OpenIdConnectOptions": {
"AuthenticationScheme": "oidc",
"SignInScheme": "Cookies",
"Authority": "http://localhost:5000",
"RequireHttpsMetadata": false,
"ClientId": "system.health.check",
"ClientSecret": "secret",
"ResponseType": "code id_token",
"GetClaimsFromUserInfoEndpoint": true,
"SaveTokens": true
}
the configure code on the startup looks like this:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
var option1 = new OpenIdConnectOptions();
Configuration.GetSection("Security:OpenIdConnectOptions").Bind(option1);
app.UseOpenIdConnectAuthentication(option1);
This works well so far and I can see that when the user is not Authenticated, he is directed to the Identity server and the token is issues.
Now the issue is that I want call form this service to be routed to another Web API service that is also protected via scope of say hierarchy as shown below:
Service to service call
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5103/api/Hierarchy/22");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var _client = new HttpClient();
var response = await _client.SendAsync(requestMessage);
return await response.Content.ReadAsStringAsync();
[ASP.Net core MVC: Web Api ]
Startup is setup same as my other service but as this is a resource service my configuration is different:
"IdentityServerAuthenticationOptions": {
"Authority": "http://localhost:5000",
"RequireHttpsMetadata": false,
"ApiName": "hierarchy",
"AllowedScopes": [
"openid",
"hierarchy"
]
the configure code on the startup looks like this:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
var option2 = new IdentityServerAuthenticationOptions();
Configuration.GetSection("Security:IdentityServerAuthenticationOptions").Bind(option2);
app.UseIdentityServerAuthentication(option2);
Now the issue is that the call does not return anything and on the console of this service I see a log that says:
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'null/resources'. Did not match: validationParameters.ValidAudience: 'hierarchy' or validationParameters.ValidAudiences: 'null'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwt, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__1.MoveNext()
when I decode the access token on my [Web App] this is what I see
{
"nbf": 1501764330,
"exp": 1501767930,
"iss": "null",
"aud": "null/resources",
"client_id": "system.health.check",
"sub": "2",
"auth_time": 1501764327,
"idp": "local",
"scope": [
"openid",
"profile"
],
"amr": [
"pwd"
]
}
so its clearly missing the Audience as well as scope healthcheck
any ideas as to why its missing the Audience or what I am missing here?
assuming your api name is "hierarchy", try passing this in Allowed Scopes in the client configuration. Here:
new Client
{
ClientId = "system.health.check",
ClientName = "System health check client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"healthcheck" <------
},
RedirectUris = { "http://localhost:5100/signin-oidc", "http://localhost:5103/signin-oidc",},
PostLogoutRedirectUris = { "http://localhost:5100/signout-callback-oidc" },
}
You can add it after healthcheck

Identityserver3 - HybridFlow not returning profile scope

I have set-up identityserver3 and MVC4 client using this tutorial. When I configured client to use 'Implicit' flow things are working as expected and I am getting back 'profile' scope. i.e. I can find claims first_name and given_name. Below my configuration code.
Client and User configuration
public static class Users
{
public static List<InMemoryUser> Get()
{
return new List<InMemoryUser>
{
new InMemoryUser
{
Username = "Bob",Password = "password",Subject = "1",
Claims = new []
{
new Claim(Constants.ClaimTypes.GivenName,"firstName"),
new Claim(Constants.ClaimTypes.FamilyName,"lastName")
}
}
};
}
}
public static class Clients
{
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
ClientId = "MVC",
ClientName = "MVC Client Name",
RedirectUris = new List<string>
{
"https://localhost:44302/"
},
Flow = Flows.Implicit,
AllowAccessToAllScopes = true
}
};
}
}
Identity Server Configuration
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.Map("/identity", appBuilder => {
appBuilder.UseIdentityServer(new IdentityServer3.Core.Configuration.IdentityServerOptions
{
SiteName = "Site Name",
SigningCertificate = LoadCertificate(),
RequireSsl = false,
Factory = new IdentityServer3.Core.Configuration.IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryUsers(Users.Get())
.UseInMemoryScopes(StandardScopes.All)
});
});
app.UseCookieAuthentication(new Microsoft.Owin.Security.Cookies.CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44302/identity",
ClientId = "MVC",
RedirectUri = "https://localhost:44302/",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
Scope = "openid profile"
});
}
In my MVC application I have secured Action on Home controller named 'Contact'
[Authorize]
public ActionResult Contact()
{
ClaimsPrincipal principal = User as ClaimsPrincipal;
return View(principal.Claims);
}
And finally here is simple view
#model IEnumerable<System.Security.Claims.Claim>
#foreach (var item in Model)
{
<div>
<span>#item.Type</span>
<span>#item.Value</span>
</div>
}
</div>
Now when I run this app, after clicking on secure 'Contact' link I am being redirected to STS server and after providing credentials I can see below output.
Note that claims given_name and family_name exists in the claims returned by STS.
Problem:
The moment I change Client to support Hybrid flow. I am not getting back claims given_name and family_name
I made below changes to my code.
Client configuration
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
ClientId = "MVC",
ClientName = "MVC Client Name",
RedirectUris = new List<string>
{
"https://localhost:44302/"
},
Flow = Flows.Hybrid,//Changed this to Hybrid
AllowAccessToAllScopes = true
}
};
}
Server Configuration
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44302/identity",
ClientId = "MVC",
RedirectUri = "https://localhost:44302/",
ResponseType = "code id_token token", //Changed response type
SignInAsAuthenticationType = "Cookies",
Scope = "openid profile"
});
After running applicaton I can see below claims returned by STS
Note that claims given_name and family_name are missing this time.
Have I missed anything?
When you only ask for an id_token all the claims for the user are in the id_token. When you change your request to get a token (either by asking for code or token) then only the user claims configured as "AlwaysInclude" are included in the id_token. The rest must be retrieved from the user info endpoint using the access_token you received. You can use the helper APIs in the IdentityModel library to easily access the user info endpoint. Our samples show how you can do this: https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/MVC%20OWIN%20Client%20(Hybrid)/Startup.cs#L66