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

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...

Related

Connect Appwrite auth with ASP.NET Core 7

I am trying to authenticate an Appwrite user with my ASP.NET Core 7 Web API. In the past, I used Firebase for this with which I was able to implement the function as following:
private static void ConfigureFirebaseAuthentication(IServiceCollection services,
IConfiguration configuration)
{
var options = new AppOptions() { Credential = GoogleCredential.FromFile("firebase-config.json") };
FirebaseApp.Create(options);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
opt =>
{
opt.IncludeErrorDetails = true;
opt.Authority = configuration["FirebaseAuthentication:ValidIssuer"];
opt.TokenValidationParameters = new()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = configuration["FirebaseAuthentication:ValidIssuer"],
ValidAudience = configuration["FirebaseAuthentication:ValidAudience"]
};
}
);
}
This validated the request against the firebase API, but I don't see how I am able to implement something similar for Appwrite. Also the docs don't mention anything helpful.
Does anyone know how to achieve this?
Unfortunately, Appwrite doesn't have a .NET SDK yet so you would have to manually make the API call. I don't know .NET very well, but I generated code using the API specs and Insomnia:
var client = new HttpClient();
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://[HOSTNAME]/v1/account/sessions/email"),
Headers =
{
{ "X-Appwrite-Project", "[PROJECT ID]" },
},
Content = new StringContent("{\n \"email\": \"[EMAIL]\",\n \"password\": \"[PASSWORD]\"\n}")
{
Headers =
{
ContentType = new MediaTypeHeaderValue("application/json")
}
}
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
If this is successful, you can grab the X-Fallback-Cookies response header and use that for future requests.
Otherwise, if you don't want to create a session server side and you have an Appwrite JWT token generated from your front end, you can make API calls to Appwrite and pass the JWT token in the X-Appwrite-JWT header to make requests on behalf of the user.
For more information on working directly with the Appwrite REST API, refer to the REST docs.

identity server 4, Asp.net identity and

I am trying to integrate identity server 4 with asp.net identity, the documentation is very good https://identityserver4.readthedocs.io/en/latest/quickstarts/6_aspnet_identity.html
But I would like to be able to make the connection without going through the login page, but to make a direct access via a simple GET while passing the parameters.
I found this article: https://damienbod.com/2017/04/14/asp-net-core-identityserver4-resource-owner-password-flow-with-custom-userrepository/
with this method
var response = await _httpClient.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = _disco.TokenEndpoint,
ClientId = "resourceownerclient",
ClientSecret = "dataEventRecordsSecret",
Scope = "email openid dataEventRecords offline_access",
UserName = user,
Password = password
});
But can't make it work with Postman
I have an "invalid_request" error
Here is the client's statement:
new Client
{
ClientId = "resourceownerclient",
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = 3600,
IdentityTokenLifetime = 3600,
UpdateAccessTokenClaimsOnRefresh = true,
SlidingRefreshTokenLifetime = 30,
AllowOfflineAccess = true,
RefreshTokenExpiration = TokenExpiration.Absolute,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
AlwaysSendClientClaims = true,
Enabled = true,
ClientSecrets= new List<Secret> { new Secret("dataEventRecordsSecret".Sha256()) },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"dataEventRecords"
},
AllowAccessTokensViaBrowser=true
}
What is the recommended way to be able to use Asp.net Identity in this way?
You are missing grant_type in your postman request:
POST /connect/token
client_id=resourceownerclient&
client_secret=dataEventRecordsSecret&
grant_type=password&
username=damienbod&
password=damienbod&
scope=email%20openid%20dataEventRecords%20offline_access
Not sure if this is the only issue you have but it definitely one of them. The identityserver logs will contain more details of what is wrong with the request if this is not the only problem.

asp.net mvc login from identityserver4 with a invalid_request error

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"

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

"Unexpected code_verifier" when trying to achieve authorization in hybrid flow (with PKCE) using IdentityServer4

I'm trying to implement native clients (.NET Console applications as a mockup at first) to authenticate using OpenID Connect against IdentityServer4 as my STS. I use IdentityModel.OidcClient2 as my client library.
I chose to implement the code based authentication flow.
I am able to pass through the authentication stage but when I get to the authorization stage I get an error message at the client saying
invalid_grant
At the IdentityServer the error message is
"Unexpected code_verifier: XXXXXXXXXXX...."
Even though when I open fiddler and look at the requests and the debug info - the code verifier sent to the IdentityServer for the authorization seems as the client generated at first in the AuthorizationState class.
If I execute with AuthorizationState.CodeVerifier = null then it works.
But I do want to implement the PKCE for extra security. How can I achieve that?
Here is the configuration of that specific client
Identity Server :
new Client
{
ClientId = "nativeapp1",
ClientName = "Native App Demo - 1",
AllowedGrantTypes = GrantTypes.Hybrid,
RequireConsent = true,
ClientSecrets =
{
new Secret("some-secret1".Sha256())
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"custom.name",
"api1"
},
RedirectUris = {"http://127.0.0.1:7890/"},
//PostLogoutRedirectUris = {"" }
AllowOfflineAccess = true
}
And the client configuration
var options = new OidcClientOptions
{
Authority = _authority,
ClientId = "nativeapp1",
RedirectUri = redirectUri,
Scope = "openid profile api1 custom.name offline_access",
FilterClaims = true,
LoadProfile = false,
Flow = OidcClientOptions.AuthenticationFlow.Hybrid,
ClientSecret = "some-secret1"
};
You need to set RequirePkce to true on you client configuration in IdentityServer.