I have a OWIN Web Api that requires ida:Audience param to validate the JWT
public static void ConfigureAuth(IAppBuilder app)
{
var tvps = new TokenValidationParameters
{
ValidAudience = Config.GetSetting("ida:Audience"),
ValidateIssuer = false,
};
This means that I can only have a single Client App that can connect to my Web Api. There seems to be no way to add additional Audiences.
Hence, if I wan to connect other client apps to my web api, i have to change the Audience..
This doesn't make much sense...
When your API receives an Access Token, the Audience Claim aud in that token should be for your API. This means that no matter which client calls your Web API, the audience claim should be the same.
If you want to uniquely identify the client application calling your web api, you should look at the appid claim.
Let me know if this helps!
Related
I have been banging my head against the wall for a few days now. The solution is probably too simple to state in blogs so I ask the question here.
I am developing a .NET Core Web API which should delegate all authentication and authorization to a Keycloak identity provider server.
I have written the following code in my Startup.cs file:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.Authority = "https://idp.abc.xyz/auth/realms/master";
o.Audience = "products-api";
});
services.AddAuthorization(options =>
{
options.AddPolicy("Administrator", policy => policy.RequireClaim("user_roles", "product_catalog_admin"));
options.AddPolicy("User", policy => policy.RequireClaim("user_roles", "product_catalog_user"));
});
Now I can use Postman to request a token from the IDP and send that token to the Web API. Then the Web API validates that token but does NOT know anything about the IDP other than the URL and only makes a request to a public URL of the IDP to get some configuration.
Question:
HOW does the Web API know that the token is valid, not tampered with (created using different key), if it doesn't know anything about the IDP?
AddJwtBearer will use the options you give it to perform in memory validation of tokens. By default this involves the following actions:
Validate issuer
Validate audience
Check that the token's exp claim is not in the past (expired)
Verify the access token's digital signature
The 4th check is the most complex and by default this involves downloading token signing public keys from the IDP's JWKS endpoint, then choosing the one in the JWT's kid header. A blog post of mine has some details on how this works.
Of course you should always test the above 4 conditions and ensure that in each case API access is denied with a 401 error response that clients can code against.
I am trying to figure out a way to create a JWT and sign it with the service account's private key and
Send the signed JWT in a request to the Google API Endpoint. I have search out there are numerous of the library available for Java and Python but is there any library available for PHP?
will need to follow Google’s Cloud Endpoints standard for authentication between services. Below there is an example of how we can access java, which I wanted to accomplish in PHP?
public static String generateJwt(final String saKeyfile, final String saEmail,
final String audience, final int expiryLength)
throws FileNotFoundException, IOException {
Date now = new Date();
Date expTime = new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiryLength));
// Build the JWT payload
JWTCreator.Builder token = JWT.create()
.withIssuedAt(now)
// Expires after 'expiraryLength' seconds
.withExpiresAt(expTime)
// Must match 'issuer' in the security configuration in your
// swagger spec (e.g. service account email)
.withIssuer(saEmail)
// Must be either your Endpoints service name, or match the value
// specified as the 'x-google-audience' in the OpenAPI document
.withAudience(audience)
// Subject and email should match the service account's email
.withSubject(saEmail)
.withClaim("email", saEmail);
// Sign the JWT with a service account
FileInputStream stream = new FileInputStream(saKeyfile);
GoogleCredential cred = GoogleCredential.fromStream(stream);
RSAPrivateKey key = (RSAPrivateKey) cred.getServiceAccountPrivateKey();
Algorithm algorithm = Algorithm.RSA256(null, key);
return token.sign(algorithm);
}
Using PHP to create authenticated request to an Google Endpoints
For PHP seems to not be a good solution since is not provided by the Google's Cloud documentation, as you can see here.
Nonetheless, there are some documentation regarding how you can use PHP within Cloud Endpoints via the JWT's client, as you can see here, and also here.
If neither of those fit your needs you can always use a custom method to authenticate users. As you know to authenticate a user, a client application must send a JSON Web Token (JWT) in the authorization header of the HTTP request to your backend API.
As a consequent, you could use the Extensible Service Proxy (ESP):
The Extensible Service Proxy (ESP) validates the token on behalf of your API, so you don't have to add any code in your API to process the authentication. However, you do need to configure your OpenAPI document to support your chosen authentication methods.
You could see how to implement custom method authentication for users here..
Finally, in case you are interested I link some other authentication methods that you could use with your Cloud Endpoints services in case none of the above fit your needs.
I hope it helps.
I have already implemented various REST-APIs using the Serverless Framework with APIG, DynamoDB as data storage and Cognito for user authentication with Angular2 as frontend. The functions can easily be secured by a Cognito authorizer on the server-side. The downside is that I have to integrate AWS SDK in my frontend applications in order to authenticate the user with Cognito first (signup/signin, ...). I could also use the AWS_IAM authorizer but then I also have to sign all requests on the client side with an AWS specific signature before sending the request to the API Gateway.
Now I was wondering if there is any possibility to keep authentication and authorization on the server side, so I can use an open standard like JSON Web Tokens for signup/signin? This would allow me to open my REST-API for other developers as well without forcing them to use Cognito at all.
I know that one possibility would be to implement a custom authorizer for my lambda functions but isn't there anything stable, which can be used "out-of-the-box" already? Most of the examples I found are using Cognito or IAM auth AWS signature signin on client side (e.g. serverless-stack.com).
It is curious that I didn't find any useful informations about this on the web so far, since I think that this is a typical use case for REST APIs. Or do I have a conceptual misunderstanding about API Gateway + Cognito?
I have been through the same trouble in understanding the way how AWS Cognito works and what options are available to implement authentication & authorization. Unfortunately there is no out-of-the-box method available to do it for your requirement. Nevertheless let's hope that Amazon comes up with a feature very soon.
Basically, there are 3 options available to implement authentication.
AWS_IAM
Cognito Authorizer
Custom Authorizer
AWS_IAM
In addition to authentication, this method can be used to implement authorization using IAM Roles or IAM Users easily. The only downside of it is that you need to send a request signed with an aws-signature-4 which is not the standard way that we have seen in IDP services like Auth0.
Cognito Authorizer
This method meets the expectation of sending a JWT token with API requests. You can create users in Cognito User Pool and then use it to authenticate and generate an IdToken. However, this method will only allow you to authenticate users; authorization needs to be handled in method level.
Custom Authorizer
This method can be used to write your own way of authentication and authorization. Also it helps to eliminate writing authorization logic in API methods. The ideal solution would be to use AWS Cognito User Pool to authenticate users and then generate a policy document for IAM Role to access resources.
Here is an example AWS cognito userpools JavaScript SDK get user's policy documents.
Also keep in mind that this solution will be invoking an extra lambda function for each request that you make.
You can use Cognito Auth to Server-side. Following would be the steps.
Implementing Sign-up and Sign-in
Implement Sign-up form in the frontend and API Gateway endpoint(e.g /register) using Lambda to receive, the Sign-up data, which will create user in Cognito using AWS SDK. For detailed reference check this link.
AWSCognito.config.region = 'us-east-1'; //This is required to derive the endpoint
var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var attributeList = [];
var dataEmail = {
Name : 'email',
Value : 'email#mydomain.com'
};
var dataPhoneNumber = {
Name : 'phone_number',
Value : '+15555555555'
};
var attributeEmail = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataEmail);
var attributePhoneNumber = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataPhoneNumber);
attributeList.push(attributeEmail);
attributeList.push(attributePhoneNumber);
userPool.signUp('username', 'password', attributeList, null, function(err, result){
if (err) {
alert(err);
return;
}
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
});
Do similarly for the Sign-in by creating an frontend & API Gateway endpoint(e.g /login)
var authenticationData = {
Username : 'username',
Password : 'password'
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var userData = {
Username : 'username',
Pool : userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
/* Use the idToken for Logins Map when Federating User Pools with Cognito Identity or when passing through an Authorization Header to an API Gateway Authorizer */
console.log('idToken + ' + result.idToken.jwtToken);
},
onFailure: function(err) {
alert(err);
},
});
Storing and sending the JWT from your Browser and Validating at API Gateway.
After receiving the JWT from the Sign-in API endpoint, you can store it locally in user's browser, using HTML5 Localstorage, Sessionstorage or Client Side Cookie. Optionally if you need to use Server Side Cookies, it will require to have a Proxy backend which keeps the Session state with the Web App and Convert it to the JWT to invoke the API Gateway.
From the Web Browser(Assuming your client directly invokes API Gateway) set a HTTP header called Authorization and forward the JWT to the API Gateway invocations.
At API Gateway use Cognito Authorizer to as the Authorize the token where it will also forward the user identity resolved to your Lambdas.
Note: Here I have purposefully avoided the IAM Authorization since it will require some additional work from Web App JavaScripts to implement Signature 4 Signing at Browser and also requires to refresh the token frequently which is straightforward with AWS JavaScript SDKs but will become complex if you need to implement it on your own.
Please take a look at this here.
The example demonstrates various configurations that include custom authorizers, cognito, lambda, dynamoDB etc.
I am adding IdentityServer3 on to an existing website (NopCommerce to be specific). It has it's own registration and authentication system, but we need to also offer OpenID Connect so that a back end application can be integrated. Calls to the back end need to have an id_token for the current user that the back end validates to confirm the identity.
I've found information about how to use an existing membership database to provide the user data for IdentityServer3 to check, however I am bit stuck on how to generate the id_token for each user. I guess the obvious answer is to replace the website login with IdentityServer, however that raises further issues for the rest of the project. Ideally I'd like the user to log in as normal and then call a method in IdentityServer to generate the id_token.
Is this possible? I've been hunting around, but can't find anything so far. The best I found was an answer to programmatically sign in to identityserver3. I think it's suggesting making a HTTP post to IdentityServer, but it feels kind of hacky.
I also found Implementing OAuth 2.0 and OpenId Connect provider using IdentityServer3 with existing login server and membership provider, but I have to admit it's assuming quite a bit of knowledge I don't have (yet).
My solution based on John C's answer worked using a NopCommece external authentication plugin, but I was unhappy with having to leave Nop to authenticate and register when IdentityServer was using the Nop database. Going via the external auth route seemed to be the only way to get an OpenID Connect id_token. After a break and some time to revisit the code though I found the following:
https://identityserver.github.io/Documentation/docsv2/configuration/serviceFactory.html
https://identityserver.github.io/Documentation/docsv2/configuration/serviceFactory.html
By implementing custom services, IdentityServer allows you to mess with the token creation and generation AND the dependency injection system it uses gives you access to instatiated versions of the default services.
Previously I had followed an answer which sent a username and password to the token endpoint. The OpenID specs say that this should only return the access_token, which is exactly what DefaultTokenService in IdenttyService does. By adding a CustomTokenResponseGenerator however, I was able to re-use the request to create and return an id_token too.
CustomTokenResponse class:
internal class CustomTokenResponseGenerator : ICustomTokenResponseGenerator
{
protected ITokenService _tokenService;
public CustomTokenResponseGenerator(ITokenService tokenService)
{
_tokenService = tokenService;
}
public Task<TokenResponse> GenerateAsync(ValidatedTokenRequest request, TokenResponse response)
{
var tokenRequest = new TokenCreationRequest
{
Subject = request.Subject,
Client = request.Client,
Scopes = request.ValidatedScopes.GrantedScopes,
//Nonce = request.AuthorizationCode.Nonce,
ValidatedRequest = request
};
var idToken = _tokenService.CreateIdentityTokenAsync(tokenRequest);
idToken.Wait();
var jwt = _tokenService.CreateSecurityTokenAsync(idToken.Result);
response.IdentityToken = jwt.Result;
return Task.FromResult(response);
}
}
How to inject the custom service in Startup.cs:
factory.TokenService = new Registration<ITokenService, TokenService>();
factory.CustomTokenResponseGenerator = new Registration<ICustomTokenResponseGenerator, CustomTokenResponseGenerator>();
When user logs-in into NopCommerce application, you can send an HTTP authorize request to identityserver. Make sure while sending the authorize request to idsrv you are using prompt=none, this way you will get the id_token or access_token without showing a consent to the user again if the user is already logged-in.
function getAuthorizeRequest() {
var url = global.appSettings.identityServerURL
+ "/connect/authorize?client_id=siteB&response_type=id_token token&redirect_uri="
+ global.appSettings.siteBUrl + "/Main/NopCommerceapp&scope=siteBscope openid email roles&prompt=none&nonce="76767xz676xzc76xz7c67x6c76"
return encodeURI(url);}
Checkout idsrv authorize endpoint https://identityserver.github.io/Documentation/docsv2/endpoints/authorization.html
I think your best solution would be to implement IdentityServer3 and get it reading from your existing Nop membership database. Then create a Web API app that runs on its own that utilizes your IdentityServer3 setup. Inside the Web API, you implement all the functionality that your back end app needs, reading and writing to the Nop database.
Keep your Nop frontend UI separate from your backend API. If you follow the two links below, you should be able to get something up and running pretty quickly.
Creating the simplest OAuth2 Authorization Server, Client and API
MVC Authentication & Web APIs
I have question regarding setup of access token renewal/refresh. Our Setup:
Implicit flow
Angular SPA using bearer token for API
Thin MVC frontend serving cshtml containing SPA
Short access token (10min)
20 min Idsrv cookie sliding (used as activity timeout)
The application has to apply to some strict security rules and intellectual property.
We need to renew the access token before it expires and API returns 401.
I’ve looked at the oidc-client-js to handle that. But that would remove the option of authenticating the static files like we do today, since there would no longer be a cookie for the MVC app.
Is there a way of securing them, or is that just something that we have to accept when building a SPA with OpenID Connect?
If you would like to enforce authorization on static files then this needs to be done by server-side code. Since your client is using an MVC backend, my recommendation would be to use the Hybrid Flow in conjunction with the Katana OpenID Connect middleware. You may then pass on any tokens you would like to use from the server-side code to your SPA via your view (cshtml).
The middleware required is available on NuGet:
install-package Microsoft.Owin.Security.Cookies
install-package Microsoft.Owin.Security.OpenIdConnect
The following snippet allows for configuration in your OWIN pipeline (taken and slightly altered from this tutorial):
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44319/identity",
ClientId = "mvc",
RedirectUri = "https://localhost:44319/",
ResponseType = "code id_token",
Scope = "openid offline_access",
SignInAsAuthenticationType = "Cookies"
});
Using this flow, you are no longer given tokens immediately but will need to exchange the auth code returned ("code" grant type) for a pair of tokens being the:
access_token (the one you are already receiving in implicit flow)
refresh_token (this can be used at the token endpoint to renew the access_token)
The main things to note about the above configuration are response type and scope.
Response type is no longer just asking for tokens (implicit) but now asks for code.
Scope includes "offline_access" scope which will return the refresh_token.
Like this we have solved both your access token expiry problem and have begun to solve your static asset problem by moving authorization to the back-end. To help you with the next part I would need to know how you are serving your assets as there are different ways depending on whether you are using OWIN to serve or IIS.