Question about ASP.NET Core 3 Identity / Identity Server / SPA support for Resource Owner Password Grant Type - asp.net-core

As per Authentication and authorization for SPAs, I have created a new SPA with support for API authorization. You can view this on GitHub.
In order to support integration tests, I have added a new client (see appsettings.json) that is allowed the resource owner password grant type:
"SecureSpa.IntegrationTests": {
"Profile": "IdentityServerSPA",
"AllowedGrantTypes": [ "password" ],
"ClientSecrets": [ { "Value": "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=" } ],
"AllowedScopes": [ "SecureSpaAPI", "openid", "profile" ]
}
Then within WeatherForecastControllerTests.cs, I attempt to request the token as follows:
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "SecureSpa.IntegrationTests",
ClientSecret = "secret",
Scope = "SecureSpaAPI openid profile",
UserName = "demouser#securespa",
Password = "Pass#word1"
});
When running the test, I've tried many different combinations, however the results are usually the same (unauthorized_client). This is the relevant log output from Identity Server:
IdentityServer4.Endpoints.TokenEndpoint: Debug: Start token request.
IdentityServer4.Validation.ClientSecretValidator: Debug: Start client validation
IdentityServer4.Validation.BasicAuthenticationSecretParser: Debug: Start parsing Basic Authentication secret
IdentityServer4.Validation.PostBodySecretParser: Debug: Start parsing for secret in post body
IdentityServer4.Validation.SecretParser: Debug: Parser found secret: PostBodySecretParser
IdentityServer4.Validation.SecretParser: Debug: Secret id found: SecureSpa.IntegrationTests
IdentityServer4.Stores.ValidatingClientStore: Debug: client configuration validation for client SecureSpa.IntegrationTests succeeded.
IdentityServer4.Validation.ClientSecretValidator: Debug: Public Client - skipping secret validation success
IdentityServer4.Validation.ClientSecretValidator: Debug: Client validation success
IdentityServer4.Events.DefaultEventService: Information: {
"Name": "Client Authentication Success",
"Category": "Authentication",
"EventType": "Success",
"Id": 1010,
"ClientId": "SecureSpa.IntegrationTests",
"AuthenticationMethod": "SharedSecret",
"ActivityId": "0HLPN4PPDDMCJ",
"TimeStamp": "2019-09-12T02:10:57Z",
"ProcessId": 28948,
"LocalIpAddress": "unknown",
"RemoteIpAddress": "unknown"
}
IdentityServer4.Validation.TokenRequestValidator: Debug: Start token request validation
IdentityServer4.Validation.TokenRequestValidator: Debug: Start resource owner password token request validation
IdentityServer4.Validation.TokenRequestValidator: Error: Client not authorized for resource owner flow, check the AllowedGrantTypes setting{ client_id = SecureSpa.IntegrationTests }, details: {
"ClientId": "SecureSpa.IntegrationTests",
"ClientName": "SecureSpa.IntegrationTests",
"GrantType": "password",
"Raw": {
"grant_type": "password",
"username": "demouser#securespa",
"password": "***REDACTED***",
"scope": "SecureSpaAPI",
"client_id": "SecureSpa.IntegrationTests",
"client_secret": "***REDACTED***"
}
}
IdentityServer4.Events.DefaultEventService: Information: {
"Name": "Token Issued Failure",
"Category": "Token",
"EventType": "Failure",
"Id": 2001,
"ClientId": "SecureSpa.IntegrationTests",
"ClientName": "SecureSpa.IntegrationTests",
"Endpoint": "Token",
"GrantType": "password",
"Error": "unauthorized_client",
"ActivityId": "0HLPN4PPDDMCJ",
"TimeStamp": "2019-09-12T02:10:57Z",
"ProcessId": 28948,
"LocalIpAddress": "unknown",
"RemoteIpAddress": "unknown"
}
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 212.96790000000001ms 400 application/json; charset=UTF-8
Is this approach supported? If not, is there an alternative approach that can be used to get the token in order to write integration tests? I'm planning to set up test users along with the test client so that I can test lots of different behaviours.

I continued working on this issue and found that the allowed grant type of password was not being added when the profile is set to IdentityServerSPA. I couldn't see a way to add a client without a profile via appsettings, so I removed the configuration from appsettings and created the clients using this approach:
services.AddIdentityServer()
//.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddIdentityServerSPA("SecureSpa", builder =>
{
builder.WithRedirectUri("https://localhost:44307/authentication/login-callback");
builder.WithLogoutRedirectUri("https://localhost:44307/authentication/logout-callback");
});
options.Clients.Add(new Client
{
ClientId = "SecureSpa.IntegrationTests",
AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = { "SecureSpaAPI", "openid", "profile" }
});
});
With that in place my tests now run. You can see the final solution here; https://github.com/JasonGT/SecureSpa/.
Everything works fine, however there seems to be a bug (or feature limitation) within DefaultClientRequestParametersProvider. See the 'GetClientParameters' method - if the specified client does not have an associated profile, an InvalidOperationException is thrown.
Let me know if you need more information.

Just for reference' sake: the code above did not work as-is on my end, it broke the SPA sign-in with a redirect_uri invalid exception.
I had to remove the base url, and then it worked:
builder.WithRedirectUri("/authentication/login-callback");
builder.WithLogoutRedirectUri("/authentication/logout-callback");

Related

Adding two jwt validators to api endpoint through krakenD

I have two authentication mechanisms that I need to enable through proxy using krakenD. Each authentication has their own jwk-url to validate the keys of the token. I am using krakenD community edition 1.3 and following the krakenD documentation for jwt validation here https://www.krakend.io/docs/v1.3/authorization/jwt-validation/. Is there a way to add two jwt validators to the same endpoint?
{
"endpoint": "/paas/hydra/test/ab/projects/{project}",
"method": "GET",
"output_encoding": "no-op",
"headers_to_pass": ["Authorization"],
"extra_config": {
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
"jwk-url": "{{ .jwk.host }}/oauth2/v1/keys",
"cache": true,
"cache_duration": 1800,
"disable_jwk_security": true
},
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
"jwk-url": "https://authbluetokens.aexp.com/v2/app2app/tokens/keys",
"cache": true,
"cache_duration": 1800,
"disable_jwk_security": true
}
},
"backend": [
{
"method": "GET",
"encoding": "no-op",
"host": ["http://localhost:8080"],
"url_pattern": "/v6/workspaces/{project}",
"extra_config": {
"github.com/devopsfaith/krakend/transport/http/client/executor": {
"name": "bomoktacustom",
"audienceid": "0oasx1emolGCrwnht0x7,0oasx1nyh6ytqb96z0x7,0oaohi3lo9nr9lMXu0x7,0oarc7drtoyMIbAic0x7,0oaqnh8rpuaCUFhD70x7,0oaqctxj5rYvoUVuy0x7,0oappvfm4hre783rH0x7,0oawdujgrqi4DPyBz0x7,*.aexp.com"
},
"github.com/devopsfaith/krakend-ratelimit/juju/proxy": {
"maxRate": 6,
"capacity": 6
},
"github.com/devopsfaith/krakend-martian": {
"header.Modifier": {
"scope": ["request", "response"],
"name": "Content-Type",
"value": "application/json"
}
}
}
}
]
},
As shown in the code, I have tried adding two krakend-jose/validator to the same endpoint. The current behavior of this implementation is krakenD ignores the first validator and only utilizes the second one. When using a token that requires the first validator krakenD returns Error #01: no Keys has been found. But using a token with the second validator works. The behavior I need is krakenD allowing to validate both type of tokens. Any help would be much appreciated!!
The functionality you are looking for works only in the enterprise edition: https://www.krakend.io/docs/enterprise/authentication/multiple-identity-providers/ (it was also available on EE v1.3)
The community version expects 1 identity provider per endpoint only. Duplicating the keys or any other approach is not going to have the desired outcome.

Microsoft Graph API - 401 Unauthorized While Creating Chat

I using the graph API v1.0 to create the one-to-One chat,but I found some account get 401 results.I check the Permission from the Api '/me/oauth2PermissionGrants',and I can find the 'Chat.Create' permission.
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#oauth2PermissionGrants",
"value": [
{
"clientId": "9fc4855b-4d7e-443b-a8f0-282690a55a73",
"consentType": "Principal",
"id": "W4XEn35NO0So8CgmkKVacwsSh1fUKDpBhq1AxEmfwg74z92MWfk9ToJPj-kyp8I8",
"principalId": "8cddcff8-f959-4e3d-824f-8fe932a7c23c",
"resourceId": "5787120b-28d4-413a-86ad-40c4499fc20e",
"scope": " offline_access openid profile email User.Read User.ReadWrite User.ReadBasic.All Chat.ReadWrite Chat.Create Presence.Read Presence.Read.All ChatMessage.Read ChatMessage.Send"
},
{
"clientId": "d39356e1-6d20-4d19-ad54-5278e19b94ec",
"consentType": "Principal",
"id": "4VaT0yBtGU2tVFJ44ZuU7AsSh1fUKDpBhq1AxEmfwg74z92MWfk9ToJPj-kyp8I8",
"principalId": "8cddcff8-f959-4e3d-824f-8fe932a7c23c",
"resourceId": "5787120b-28d4-413a-86ad-40c4499fc20e",
"scope": " offline_access openid profile email User.Read User.ReadWrite User.ReadBasic.All Chat.ReadWrite Chat.Create Presence.Read Presence.Read.All ChatMessage.Read ChatMessage.Send"
},
{
"clientId": "c81831d1-608e-43fa-abd4-3a09e523cb3c",
"consentType": "Principal",
"id": "0TEYyI5g-kOr1DoJ5SPLPAsSh1fUKDpBhq1AxEmfwg74z92MWfk9ToJPj-kyp8I8",
"principalId": "8cddcff8-f959-4e3d-824f-8fe932a7c23c",
"resourceId": "5787120b-28d4-413a-86ad-40c4499fc20e",
"scope": " offline_access openid profile email User.Read User.ReadWrite User.ReadBasic.All Chat.ReadWrite Chat.Create Presence.Read Presence.Read.All ChatMessage.Read ChatMessage.Send Files.Read Files.ReadWrite Files.Read.All Files.ReadWrite.All Sites.Read.All Sites.ReadWrite.All"
}
]
}
When I post "https://graph.microsoft.com/v1.0/chats",
set the body like this
{"members":[
{ "user#odata.bind":"https://graph.microsoft.com/v1.0/users('8cddcff8-f959-4e3d-824f-8fe932a7c23c')",
"#odata.type":"#microsoft.graph.aadUserConversationMember",
"roles":["owner"]},
{ "user#odata.bind":"https://graph.microsoft.com/v1.0/users('636f150e-f73c-44d6-be0c-4d543b2b4e5d')",
"#odata.type":"#microsoft.graph.aadUserConversationMember",
"roles":["owner"]
}
],
"chatType":"oneOnOne"
}
It response 401 Authentication failed
{
"error": {
"code": "Unauthorized",
"message": "Authentication failed.",
"innerError": {
"date": "2022-05-17T08:41:05",
"request-id": "de03512e-b97d-4229-b1ce-a73a61ed4f3d",
"client-request-id": "de03512e-b97d-4229-b1ce-a73a61ed4f3d"
}
}
}
When I replace another account's token & teamsUserId and try again ,It returns Ok!
Can you help me check the reason?
First of all check your app id secret and confirm
secondly check that the users you are trying to add have authorized your application.
thirdly check for access token you need scopes and permission according to the token you are sending to the graph
If you are using application token , use default scope and check whether you have added the chat.create application permission else if you are using token on behalf of user you need delegated permission for chat.create application

cochdb permissions roles always "not authorized"

I try to make a user / role authentication (session based) with CouchDB but as soon as I enter a role at a database all users and roles are can access the database -> the are not authorized.
Get the session:
POST http://myhost:1234/_session
It returns (the userCtx object):
{
"ok": true,
"name": "some_user_name",
"roles": [
"developers"
]
}
Then I added the roles to the database:
PUT http://myhost:1234/database/_security
{
"admins": {
"names": [],
"roles": []
},
"members": {
"names": [],
"roles": [
"developers"
]
}
}
and it returns {"ok":true} and I can see the permissions also in fauxton.
When I now try to access the database with
GET http://myhost:1234/database/_all_docs
it returns:
{
"error": "unauthorized",
"reason": "You are not authorized to access this db."
}
If you are using Postman, Add Following key-value in Headers
"Content-Type": "application/json",
Accept: "application/json",
Authorization: "Basic " + btoa("YOUR_ADMIN_USER:YOUR_PASSWORD")
ahhh I found the mistake, I was doing the tests with postman and there I did not recognized that the credentials was not send with the requests :-(

Oauth 2.0: I can't get profile info from Google "token info endpoint" by using id_token

I'm trying to get user info (profile,email) from Google by using OAuth 2.0.
But "token info endpoint" did not return profile info even though I set "profile" to scopes.
I followed the doc of Google sign-in for iOS. (https://developers.google.com/identity/sign-in/ios/backend-auth)
I couldn't figure out why this end point doesn't return profile info as I expected.
First I get Token by oauth2.0 (as a test, I got token using Rails)
def gmail
require 'oauth2'
client = OAuth2::Client.new(
'Client-id',
'client-secret',
{
site: 'https://accounts.google.com/',
authorize_url: 'o/oauth2/auth',
}
)
redirect_to client.auth_code.authorize_url(
redirect_uri: 'http://localhost:3000/google_signup',
scope: 'email profile',
)
end
token = client.auth_code.get_token(
params[:code],
redirect_uri: 'http://localhost:3000/gmail_signup',
and I get id_tokenfrom this token.
then, send GET request to "token info endpoint".
"https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=id_token"
but response didn't contain profile info.
{
"azp": "azp",
"aud": "aud",
"sub": "sub",
"email": "email#gmail.com",
"email_verified": "true",
"at_hash": "at_hash",
"exp": "TIMESTAMP",
"iss": "accounts.google.com",
"iat": "iat",
"alg": "alg",
"kid": "kid"
}
The response was supposed to be like this.
{
// These six fields are included in all Google ID Tokens.
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"iat": "1433978353",
"exp": "1433981953",
// These seven fields are only included when the user has granted the "profile" and
// "email" OAuth scopes to the application.
"email": "testuser#gmail.com",
"email_verified": "true",
"name" : "Test User",
"picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
"given_name": "Test",
"family_name": "User",
"locale": "en"
}

How to get user profile from id_token through API call

I have an API that a applications uses, the application sends me a id_token of a already authenticated user or Google accounts.
How can I make a API call (not java script call) in ASP.net to the google API server to get the users profile information such as name, so that I can save the user in the DB for the application.
I already have a google project with a Client ID and client secret.
So I found a way to though asp.net API get the user information that I wanted.
using (var client = new System.Net.WebClient())
{
var uri = new Uri("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + ACCESSTOKENFROMLOGGININ + "&access_type=offline");
var response = client.DownloadString(uri);
}
you response will have these fields
{
"issued_to": "",
"audience": "",
"user_id": "",
"scope": "",
"expires_in": 756,
"email": "",
"verified_email": true,
"access_type": "online"
}
Then you could make another call to get more personal information
var userInfoUri = new Uri("https://www.googleapis.com/oauth2/v2/userinfo");
var userInfoClient = new System.Net.WebClient();
client.Headers.Add("Authorization", "Bearer " + YOUACCESSToken);
and you will receive an object like this
{
"id": "",
"name": "",
"given_name": "",
"family_name": "",
"link": "",
"picture": "",`enter code here`
"locale": "en"
}