Catch ClaimsIssuer on asp.net core 2 ConfigureServices - asp.net

I am looking a way to grab the value of Claim Issuer. I want to make 2 way validation of token.
First step user will get the token from System API and pass it to Customer API. In customer API token need to be validate with system API token parameter.
if it is validated customer api generate a new token to the user and user will use the new token in next steps.
I am doing this because in the second step I will add some claim for only that user have to see and use. like connection string, user role for its own database.
Or is there any other way to do like this verification ?
thanks.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
if (????)
{
// system token validator
}
else
{
//customer token validator
}
});

Found a way to handle the problem
Just add two claim issuer and two key to validation parameter so I don't need to check anymore the claim issuer
var issuers = new List<string>()
{
Configuration["SystemToken:Issuer"],
Configuration["CustomerToken:Issuer"]
};
SecurityKey SystemKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SystemToken:Key"]));
SecurityKey CustomerKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["CustomerToken:Key"]));
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuers = issuers,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = new List<SecurityKey>() { SystemKey, CustomerKey },
ValidateLifetime = true
};
thanks you for helping
#Tratcher
https://github.com/aspnet/Security/issues/1604

Related

How to retrive a token from Cognito?

I have a simple Dotnet core API that gets value1,value2 and the method is [Authorize].
I am trying to retrieve token back from Cognito idp in order to access the result but the thing is I couldn't find a way to send the (username, pass, and email)to Cognito in order to get back a token.
I keep getting the following error:
Error in SAML response processing: Invalid user attributes: email: The attribute is required ', error_uri: 'error_uri is null'
ยจ
In startup.cs I configured it as the following:
services.Configure<OpenIdConnectOptions>(Configuration.GetSection("Authentication:Cognito"));
var serviceProvider = services.BuildServiceProvider();
var authOptions = serviceProvider.GetService<IOptions<OpenIdConnectOptions>>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ResponseType = authOptions.Value.ResponseType;
options.MetadataAddress = authOptions.Value.MetadataAddress;
options.ClientId = authOptions.Value.ClientId;
options.ClientSecret = authOptions.Value.ClientSecret;
options.SaveTokens = authOptions.Value.SaveTokens;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = authOptions.Value.TokenValidationParameters.ValidateIssuer
};
});
I am expecting to get the token back after sending (username, pass, and email) attributes to Cognito idp and provide it to postman in order to get the values.
Use the below code to retrieve the access token after successful sign up with username and password
CognitoUserPool userPool=new CognitoUserPool(poolid,client_id,provider);
CognitoUser user=new CognitoUser(username,client_id,userPool,provider);
InitiateSrpAuthRequest authRequest=new InitiateSrpAuthRequest()
{
Password=password
};
Task<AuthFlowResponse> authFlowResponse=null;
authFlowResponse=user.StartWithSrpAuthAsync(authRequest);
string Token=authFlowResponse.Result.AuthenticationResult.AccessToken.ToString();

Setting up OpenID Connect for .NET Core Web API

I have a simple application which uses Angular as front-end and a .NET Core Web API as back-end services. Now I want to secure my WEB API layer. I though I can use OpenID Connect for that purpose. But all the examples or documentation online uses some identity management systems to like (Keycloak, Okta) but i just want to use my user data from a SQL database.
So something like, I hit the WEB API from Angular to get the token generated(using OpenID?) based on the user details sent. The i can just use the token to Authorize users. I want to use OpenID so that i can use some other identity management systems later if i want to.
my startup class in WEB API
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.ClientId = "sa";
o.ClientSecret = "sa";
o.Authority = "https://localhost:44352";
o.GetClaimsFromUserInfoEndpoint = true;
o.RequireHttpsMetadata = true;
});
I added a controller with Authorize attribute and added a test method to see what happens when i hit that from Swagger
I see the following error
IOException: IDX20804: Unable to retrieve document from: 'https://localhost:44352/.well-known/openid-configuration'
I am not sure what the error is.
Also I would like to ask if i doing this correct. Can i use the same API (Authority? as in ( o.Authority = "https://localhost:44352";)) to authenticate/get token from).
What you'll need for OpenIDConnect is definitely a Server that implements the oidc-spec.
The .well-known/... url is part of the oidc discovery spec, which Servers like identityserver and keycloak implement. It gives a standardized List of other endpoints to retrieve tokens, get userinfo, get logouturi etc.
Your API does not have such an endpoint, so that's what the error says.
If you want to implement the whole oidc-spec on your own, go for it, but I wouldn't recommend it, it's kind of complex. As I see it, .net core does only implement an openidconnect-client and you're free to choose the server implementation as long as it implements the spec.
Alternatively, have a look at JwtBearer, which is a more lightweight approach to authentication and more easy to implement yourself (and, best of: you could easily change to oidc later on). Good starting points might be these blogposts.
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
options.LoginPath = new PathString("/");
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.Cookie = new CookieBuilder()
{
SecurePolicy = CookieSecurePolicy.SameAsRequest,
Path = "/"
};
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(sessionTimeout);
}).AddOpenIdConnect(options =>
{
options.ClientId = Configuration.GetValue<string>("oidc:ClientId");
options.ClientSecret = Configuration["oidc:ClientSecret"];
options.CallbackPath = new PathString("/auth/callback");
options.GetClaimsFromUserInfoEndpoint = true;
options.Authority = Configuration["oidc:Authority"];
options.SignedOutRedirectUri = "/";
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.UseTokenLifetime = true;
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["oidc:ClientSecret"]));
options.TokenValidationParameters = new TokenValidationParameters
{
RequireSignedTokens = true,
IssuerSigningKey = signingKey,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
};
options.ResponseType = OpenIdConnectResponseType.Code;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("email");
options.Scope.Add("profile");
options.Events = new OpenIdConnectEvents()
{
OnTicketReceived = context =>
{
var identity = context.Principal.Identity as ClaimsIdentity;
if (identity != null)
{
if (!context.Principal.HasClaim(c => c.Type == ClaimTypes.Name) &&
identity.HasClaim(c => c.Type == "name"))
identity.AddClaim(new Claim(ClaimTypes.Name, identity.FindFirst("name").Value));
if (context.Properties.Items.ContainsKey(".TokenNames"))
{
string[] tokenNames = context.Properties.Items[".TokenNames"].Split(';');
foreach (var tokenName in tokenNames)
{
string tokenValue = context.Properties.Items[$".Token.{tokenName}"];
identity.AddClaim(new Claim(tokenName, tokenValue));
}
}
}
var cp = new ClaimsPrincipal(identity);
context.Principal = cp;
return Task.CompletedTask;
},
//OnTokenValidated = context =>
//{
// ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
// Claim Name = identity.FindFirst("preferred_username");
// Claim gender = identity.FindFirst(ClaimTypes.Gender);
// Claim sub = identity.FindFirst(ClaimTypes.NameIdentifier);
// var claimsToKeep = new List<Claim> { Name, gender, sub };
// var newIdentity = new ClaimsIdentity(claimsToKeep, identity.AuthenticationType);
// context.Principal = new ClaimsPrincipal(newIdentity);
// return Task.FromResult(0);
//},
OnAuthenticationFailed = context =>
{
context.Response.Redirect("/");
context.HandleResponse();
return Task.CompletedTask;
},
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("pfidpadapterid", Configuration["oidc:pfidpadapterid"]);
return Task.FromResult(0);
}
};
});

Bearer error="invalid_token", error_description="The signature is invalid"

I have a angular application that request a token from azure. The login went well and I get a token. This token is now send from the angular app to a net core webapi application. Net core should verify this token but failed. I think the webapi should also contact azure to validate the token because it has no knowledge of the private and public key that is needed to verify the token.
At the moment it is not clear why it is failing. Both angular app and the webapi are running local on my computer.
The error is: Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: 'IDX10500: Signature validation failed. No security keys were provided to validate the signature.'
my net core 2 config is:
var tokenValidationParameters = new TokenValidationParameters
{
RequireExpirationTime = true,
RequireSignedTokens = false,
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidIssuer = "8d708afe-2966-40b7-918c-a39551625958",
ValidateAudience = true,
ValidAudience = "https://sts.windows.net/a1d50521-9687-4e4d-a76d-ddd53ab0c668/",
ValidateLifetime = false,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Audience = "8d708afe-2966-40b7-918c-a39551625958";
options.ClaimsIssuer = "https://sts.windows.net/a1d50521-9687-4e4d-a76d-ddd53ab0c668/";
options.RequireHttpsMetadata=false;
options.TokenValidationParameters = tokenValidationParameters;
options.SaveToken = true;
});
That is quite a lot of configuration you have :)
The two mandatory settings are the Audience and Authority:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.Audience = "8d708afe-2966-40b7-918c-a39551625958";
o.Authority = "https://login.microsoftonline.com/a1d50521-9687-4e4d-a76d-ddd53ab0c668/";
});
You are missing the Authority so it does not know where to load the signing public keys from.
You are missing IssuerSigningKey property in your TokenValidationParameters. Thats why its complaining.
The simplest example would be
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("yOURsECRETkEY12345"))
I'm not sure how azure comes into play, you probably need it to retrieve security key information, if thats your signing authority
Edit:
Azure specific settings
.AddJwtBearer(options => {
options.Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/", Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]);
options.Audience = Configuration["Authentication:AzureAd:ClientId"];
});

Get claims from JWT token in ASP.NET Core 2.0 API

I'm using mixed authentication in my ASP.NET Core 2.0 Web and API app. Meaning both cookies and now adding JWT token.
The web part of the app uses cookies and in the API part, I want to use JWT token.
My question is how do I get the claims from JWT token? In my web controllers, I can simply use HttpContext.User; to get the claims stored in a cookie. How do I handle it in my API methods where I want to use JWT token?
Here's my AuthenticationBuilder:
public static void MyAuthenticationConfig(IServiceCollection services, IConfiguration configuration)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "myApp_cookie";
options.DefaultChallengeScheme = "myApp_cookie";
})
.AddCookie("myApp_cookie", options =>
{
options.AccessDeniedPath = "/Unauthorized";
options.LoginPath = "/Login";
})
.AddCookie("social_auth_cookie")
.AddOAuth("LinkedIn", options =>
{
options.SignInScheme = "social_auth_cookie";
options.ClientId = "my_client_id";
options.ClientSecret = "my_secret";
options.CallbackPath = "/linkedin-callback";
options.AuthorizationEndpoint = "https://www.linkedin.com/oauth/v2/authorization";
options.TokenEndpoint = "https://www.linkedin.com/oauth/v2/accessToken";
options.UserInformationEndpoint = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,picture-url,picture-urls::(original))";
options.Scope.Add("r_basicprofile");
options.Scope.Add("r_emailaddress");
options.Events = new OAuthEvents
{
OnCreatingTicket = OnCreatingTicketLinkedInCallBack,
OnTicketReceived = OnTicketReceivedCallback
};
})
.AddFacebook(options =>
{
options.SignInScheme = "social_auth_cookie";
options.AppId = "my_app_is";
options.AppSecret = "my_secret";
options.Events = new OAuthEvents
{
OnCreatingTicket = OnCreatingTicketFacebookCallback,
OnTicketReceived = OnTicketReceivedCallback
};
})
.AddGoogle(options =>
{
options.SignInScheme = "social_auth_cookie";
options.ClientId = "my_id.apps.googleusercontent.com";
options.ClientSecret = "my_secret";
options.CallbackPath = "/google-callback";
options.Events = new OAuthEvents
{
OnCreatingTicket = OnCreatingTicketGoogleCallback,
OnTicketReceived = OnTicketReceivedCallback
};
})
.AddJwtBearer("JwtBearer", jwtBearerOptions =>
{
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("my_secret")),
ValidateIssuer = true,
ValidIssuer = "my-api",
ValidateAudience = true,
ValidAudience = "my-client",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
});
}
Normally the claims of JWT are automatically added to the ClaimsIdentity.
Source:
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/af5e5c2b0100e8348c63e2d2bb45612e2080841e/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs#L1110).
So you should be able to just use 'User' property of the base 'Controller' class.
public async Task<IActionResult> Get()
{
// TODO Move 'Claims' extraction code to an extension method
var address = User.Claims.Where('GET THE NEEDED CLAIM');
...
}
I never had any problems getting the claims from a JWT token.
But I only used IdentityServer4.AccessTokenValidation so far. But internally it uses the Microsoft JWT Handler afaik.
Couldn't comment because my post would be to long, so making a seperate post instead.
If you follow the guide/link ErazerBrecht posted, the claims are indeed stored in the ClaimsPrincipal User. I created an extension method to retrieve the claim.
Note that I use an Enum for registering my claims. I use a dictionary to pass my claims to the method that generates my token, so my claim key should always be unique.
The extension method:
public static string GetClaim(this ClaimsPrincipal claimsPrincipal, JwtClaim jwtClaim)
{
var claim = claimsPrincipal.Claims.Where(c => c.Type == jwtClaim.ToString()).FirstOrDefault();
if (claim == null)
{
throw new JwtClaimNotFoundException(jwtClaim);
}
return claim.Value;
}
Call it like:
var userId = User.GetClaim(JwtClaim.UserId);

Token validation stops working when using identity

I've added token (jwt) validation to my Web-API by following the steps in this tutorial. However, when I now try to add Asp.Net Identity to my application it somehow skips the token validation.
The token generation still works fine.
The API-controller-action that I am trying to access looks like this:
// GET api/users/5
[HttpGet("{id}")]
[Authorize]
public User Get(int id)
{
return _userService.FindById(id);
}
The "Authorize" attribute doesn't seem to make any difference after adding identity to my OWIN startup file, which looks like this:
var secretKey = "secret";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var audience = "aud";
var issuer = "iss";
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = issuer,
ValidateAudience = true,
ValidAudience = audience,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
});
app.UseStaticFiles();
app.UseIdentity(); // When adding this the token-validation above seems to stop working...
var options = new TokenProviderOptions
{
Audience = audience,
Issuer = issuer,
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
};
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
app.UseMvc();
I've tried moving the Identity middleware above JwtBearerAuth, with no luck.
It always seems to skip validating my token, and even if I leave out the token I am able to reach the action, but when I remove the Identity middleware the token-validation starts working again.

Resources