I have two different REST API’s say Authentication API & Business API hosted in Windows azure.
Authentication API is responsible for authenticating user using WsFederation or OpenID or custom authentication
Business API is responsible for executing business logic. Only legitimate user should be able to access this API.
Client will contact first Authentication API and will acquire the token and then it will pass that token to Business API.
Since client can send any token to business API. Business has to validate the token. It simply cannot trust on the token. Since token Is generated by Authentication API, how business API will validate the token.
What is a standard way to validate the token in such scenario where Authentication API and Business API are hosted separately?
Good References
Principles of Token Validation (Vittorio's blog) - Awesome article, most of the information is generic so applies with or without Azure AD, and it's coming from a very knowledgeable author of course
Manually validating a JWT access token in a web API - This one is specific to validating an Azure AD issued token. Has complete code implementation too. You don't need to do exactly this, because your validation will depend on token format/claims that you use but may provide a helpful reference.
You can look at how token validation is recommended for applications secured by Azure Active Directory as a case study (and may be some other systems as well), then decide what works best for your case.
Brief explanation on how Azure AD example is relevant.. as validating received token is required just like in your case
When you develop any web API (or web app) secured by Azure Active Directory and use bearer token based authentication, the way things flow is very similar to what you explain above with your 2 APIs (just for understanding purpose, your authentication API is doing what Azure AD token endpoints would do.. i.e. provide a valid token).
To access the secure web API, calling application/client first interacts with Azure AD endpoints to get a relevant token for required resource, then it sends a request for actual resource along with the token in Authorization header to the web API. Now, the first thing web API does is to validate this token and only if everything is correct continue with execution to eventually return a response. I hope this matches at a high level with your flow.
What to validate?
1. Token Signature
The key used to sign the issued token is uniquely associated to the issuing authority, so nobody else can issue a token with your Authority's signature.
a. This helps to check that the token was in fact issued by Azure AD (or in your case your trusted STS, using the Authentication API you mention).
b. This also makes sure that since the token was issued and till it reached your web API, no body has tampered with it. If any attempt is made to change any information in the token, the signature will break.
2. Token Claims
This will depend on what claims/information you send as part of the token (for example, if same Authentication API issues tokens for multiple different APIs, then something like audience and issuer might make sense for you as well. Token validity time period using something like nbf and exp below are also pretty generic)
Taking Azure AD issued tokens as example here are the important ones that should be validated:
audience claim (aud), to verify that the token was intended to be given to your application
not before (nbf) and expiration time (exp) claims, to verify that the token has been received within it's validity period
issuer claim (iss), to verify that that token was in fact issued by the said authority. Notice this is a second way, apart from signature for the same purpose and generally both signature and issuer check are used together. (See Vittorio's blog)
nonce, as a token replay attack mitigation
tenant claim (tid), to verify the tenant. This is useful in case of multi-tenant applications.
Sample JWT Token from Azure AD
Actual Value: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1N... (a long encoded string continues) Decoded Value (you can check this easily using a website like https://jwt.ms or https://jwt.io):
{
"aud": "https://service.contoso.com/",
"iss": "https://sts.windows.net/7fe81447-da57-4385-becb-6de57f21477e/",
"iat": 1388440863,
"nbf": 1388440863,
"exp": 1388444763,
"ver": "1.0",
"tid": "7fe81447-da57-4385-becb-6de57f21477e",
"oid": "68389ae2-62fa-4b18-91fe-53dd109d74f5",
"upn": "frankm#contoso.com",
"unique_name": "frankm#contoso.com",
"sub": "deNqIj9IOE9PWJWbHsftXt2EabPVl0Cj8QAmefRLV98",
"family_name": "Miller",
"given_name": "Frank",
"appid": "2d4d11a2-f814-46a7-890a-274a72a7309e",
"appidacr": "0",
"scp": "user_impersonation",
"acr": "1"
}
How to do token validation?
Make use of standard libraries as much as possible. See the code implementation sample Manually validating a JWT access token in a web API. This one makes use of JwtSecurityTokenHandler.ValidateToken Method (JwtSecurityToken). Your case will depend on your token format/implementation etc.
Related
I am building an application that spans over several parts of infrastructure.
An end-user can sign in to a portal using OAuth 2.0 authorization code flow. When calling different APIs, the portal requests tokens on-behalf-of the signed in user and attaches those to outgoing requests. The tokens received to call external APIs have an aud claim matching the target API.
When a call to an external API is fired, it first passes through an API Gateway. This API Gateway validates the token and ensures the aud claim is actually intended for this endpoint. It also verifies roles and a few other claims.
When the gateway checks pass, the request is forwarded to the actual implementation. Now the actual implementation also verifies the token, but only that the token is valid (e.g. not looking at specific claims). That is not to say that the claims aren't used internally in the application after the token is validated, roles are very much part of the application logic.
So to summarize this as bullet points:
User signs in to portal
User performs some action that causes an API call
Portal attaches token to outgoing request
Request hits gateway for 1..n claims are validated
Request is forwarded to actual implementation
Implementation again validates token is valid, not looking at claims
All endpoints are public, e.g. I could call any of these from anywhere as long as I have a token.
In the above outlined scenario, I'm handling the token properly?
In general, what is the recommendation for tokens passing through several layers of an application? Should every layer validate the token? If yes, are there exceptions to the rule?
Typically, JWT tokens are validated when are sent from the client-side to the server-side. As these tokens are signed, if anyone tries to tamper with the token before sending it to the server-side endpoint, the token verification will fail, therefore these tokens are a secure way of sending the session of an authenticated user to an API or a server endpoint.
These JWTs can be also transferred between servers or different application layers, and it always would be a good practice to verify the token before processing it. It is adding a layer of security to avoid anyone sending tokens to that layer directly and skipping the validation.
We have a REST resource like this:
/customer/{customerId}/bill
We want to use the JWT tokens returned from AWS Cognito to secure access to this resource.
The {customerId} here is not the Cognito user id, it's a domain specific id. We have added this domain specific id to the Cognito user as a custom attribute. It comes in the ID token that Cognito returns like this:
{
"sub": "xxxxxxxx-852f-474d-aa9e-a50fd832bcb8",
"aud": "xxxxxxxxsijed6uf54dh0uhi",
"custom:customerId": "4044",
"event_id": "xxxxxx-fc0c-4ffc-affa-f8987714fb2b",
"token_use": "id",
....
}
If we use this ID Token in Authorization: Bearer <ID Token> we can write code (custom authoriser or in-app code) that ensures the customerId in /customer/{customerId}/bill is equal to the value of custom:customerId in the supplied token, and we have secured our API.
But then we read that you should not use ID tokens to secure APIs . The key point being:
"The audience (the aud claim) of the (ID) token is set to the application's identifier, which means that only this specific application should consume this token."
So it seems we need to send an Access Token to secure the API. With Cognito, there is no way we can add any concept of who the user is into the Access Token. We can't add a custom scope like user:4044 for example.
What folks suggest as an approach here is to call the /userinfo endpoint of Cognito on the server-side with the supplied Access Token to learn who the user is. This would enable us to write code (custom authoriser or in-app code) that calls this endpoint and asserts permission. But it's an endpoint call for every request, which seems crazy.
One thought that crossed our minds was to use the Access Token to secure access to the API itself, but also require the ID token, either as a query parameter or a header to allow us do the fine grained access control. But that too starts to feel wrong.
Surely this is a solved problem? What is the right thing to do here?
Sorry, this question is a year old, so my answer is probably irrelevant. But for future wanderer, I would say that, given the limitations of cognito in allowing custom claims in the access token, a call to the /userinfo route is definitely the best way.
The API GATEWAY lets you cache authorizer response for a given user, so you won't be calling the endpoint on every request. Note that some implementations recommend it as a way to make sure that the token haven't been revoked.
When you authenticate with an OIDC provider you get back an id token and if you specified scopes for an API you get back an access token so that client applications can make requests to protected resources on the end user's behalf. Typically the access token is also a JWT.
But what is to stop someone from spoofing one of these access tokens, and creating one and passing it to an API? I understand there are safeguards to prevent modification because the signature will be different than what any validation logic is expecting, but what if a malicious user created a brand new one manually? Especially because these tokens can be validated 'in place' by any API that requires an access token (not all API's use the introspection endpoint... especially with a JWT). I do understand there is metadata around the signing keys for JWT's from OpenID Connect providers and that it is available in the OIDC discovery document. For example, here is Google's JWK metadata. Given that you have signing information publicly available, and JWT access token's can be validated without any requests to the OIDC provider, how are JWT's secure? What is preventing a person from creating one and passing it as a bearer token to an API that requires an access token?
But what is to stop someone from spoofing one of these access tokens, and creating one and passing it to an API?
Spoofing and reconstruction of signature is nearly impossible without the private key (assuming you are using asymmetric signing algorithm like RS256) that used for signing the original JWT.
The JWK information available via OIDC discovery document only contains the public key.
Also Use HTTPS for authorization / token exchange to avoid token sniffing.
As far as i know, DC/OS has two different types of tokens:
authentication token: retrieved via a login through
https://public-master-ip/login?redirect_uri=urn:ietf:wg:oauth:2.0:oob. This token is used to retrieve api tokens.
api token: retrieved via a post call to https://public-master-ip/acs/api/v1/auth/login with the authentication token in the request body. This token is used to authorize calls against the apis. Such a token expires after 5 days.
My questions are
Are my assumptions correct?
Does a authentication token expire? If so, when and is there a way to refresh it?
Let me first define the goal of the current (1.8) Open DC/OS authentication procedure and then walk through your assumptions. I'll answer your questions after that.
Goal
The goal of the current Open DC/OS authentication procedure is to use Auth0 infrastructure for triggering a single sign-on authentication flow against one of three popular identity providers, and have the result reported back to your DC/OS cluster. If the DC/OS cluster is happy with the result, it will emit an authentication token specifically adjusted to that individual cluster.
Comments on your assumptions
authentication token: retrieved via a login through https://public-master-ip/login?redirect_uri=urn:ietf:wg:oauth:2.0:oob. This token is used to retrieve api tokens.
That's roughly true. However, what you call "authentication token" actually is an OpenID Connect ID Token emitted by an OpenID Connect identity provider.
Let us take this one slowly, as it is a little involved.
What happens behind the scenes is an OpenID Connect single sign-on authentication flow.
More precisely, the DC/OS UI displays an iframe that loads a piece of JavaScript hosted by Auth0, which -- when executed in your browser -- performs the so-called implicit flow (which is one of three specified OpenID Connect authentication flow types).
By the end of this flow(*) the JavaScript code executed in your browser receives a so-called OpenID Connect ID Token (from the identity provider, of course, which is Auth0 in this case). This token is a JSON Web Token (JWT, see RFC7519) signed with the private key of the identity provider (in this case it actually is Auth0, which basically proxies other identity providers such as Google Accounts).
The piece of JavaScript that receives the ID token then -- as you say -- POSTs the ID Token to your DC/OS cluster (to https://public-master-ip/acs/api/v1/auth/login). The receiving end is a web application behind DC/OS' Admin Router (the latter is just a pimped nginx). That web application inspects the ID Token's payload (which is JSON) and finds the key/value pair "iss": "https://dcos.auth0.com/". So it knows who (pretends to) have issued that token! Then it goes ahead and fetches https://dcos.auth0.com/.well-known/openid-configuration (wooo, where does it know that URL from? This is magic defined by OpenID Connect Discovery 1.0 and RFC5785). That JSON document there defines a JSON Web Key Set (JWKS) document (specified via RFC7517), revealing the public key corresponding to the private key that had (supposedly) signed the ID Token. So, that web application goes ahead and fetches the public key directly from the identity provider (through HTTPS). It then uses that public key to verify the cryptographic signature of the ID Token (and it checks the expiration time, too, of course). If ID Token validation passes, the DC/OS web application I talked about rightfully assumes that the user agent that had sent the ID Token is successfully authenticated against Auth0. And, trusting Auth0, we rightfully assume that the user agent is authenticated against e.g. Google Accounts.
Only then it (the small web application in DC/OS I talked about) stores the identity within DC/OS, assigns a unique user ID, and emits the DC/OS authentication token. That token refers to the given identity via the named user ID.
(*)Note that the identity provider only emits the ID Token towards your browser after you have successfully authenticated yourself towards that provider (e.g. Google Accounts) and after you have given consent to share identity details with a third-party service.
api token: retrieved via a post call to https://public-master-ip/acs/api/v1/auth/login with the authentication token in the request body. This token is used to authorize calls against the apis. Such a token expires after 5 days.
In DC/OS terminology, this is the DC/OS authentication token. It is a JWT signed with a random key only known to your DC/OS cluster. The Admin Router in your DC/OS can validate such authentication tokens. Certain HTTP requests against Admin Router are only proxied to back-end services when they contain a valid authentication token in the request (hence, this token mainly serves authentication, but also a very basic coarse-grained authorization, if you want to say so). Otherwise, Admin Router will respond with a 401 (read: "not authenticated").
Answers to your questions
Are my assumptions correct?
I hope to have clarified
that what you call "authentication token" is an OpenID Connect ID Token (a JWT).
that what you call "api token" is what's called "DC/OS authentication token" in the DC/OS ecosystem (and it's technically a JWT, too).
Does a authentication token expire?
I read this question as "Does an OpenID Connect ID Token expire?" Yes, indeed! This is what the spec says about ID Token expiration:
exp -- REQUIRED -- Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this parameter requires that the current date/time MUST be before the expiration date/time listed in the value. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time. See RFC 3339 [RFC3339] for details regarding date/times in general and UTC in particular.
Note that the spec does not enforce a particular ID Token lifetime -- this is up to identity providers to set. In case of ID Tokens emitted by dcos.auth0.com I have just confirmed that
>>> exp = 1474742063
>>> iat = 1474310063
>>> lifetime_days = (exp - iat) / (60.0 * 60 * 24)
>>> lifetime_days
5.0
That is, the ID Token emitted by Auth0 expires after 5 days.
If so, when and is there a way to refresh it?
You can only obtain a new ID Token emitted by Auth0 by going through an OpenID Connect authentication flow involving one of the configured identity providers. That is, the (only) intended way to obtain such a token and pass it along to DC/OS is triggered through the DC/OS UI which starts the Auth0-based flow for you (well, you could technically hack this together yourself...).
Note that Enterprise DC/OS offers an OpenID Connect authentication flow that
directly communicates the ID Token securely between DC/OS and the identity provider (no user agent ever sees that ID Token).
enforces the usage of the optional nonce mechanism of OpenID Connect ID Tokens (described in the spec), introducing more conceptual security on multiple levels (e.g. mitigating replay attacks).
We will probably merge that functionality into Open DC/OS by one of the next releases (no promises at this point!).
I hope that helped, let me know if there are further questions.
Folks,
What is a simplest way to track consumer applications accessing RESTful API services inside department.
We do not restrict access - no authentication/authorization - open for invocation, trusted environment.
No tools like OAuth AuthZ servers or API management yet... but might be heading there at some point.
For now we thought to request consumers just to include some custom HTTP Header like X-Client-Id and log it on the server side for stats etc..
But knowing that in the future we might want to switch to more standard ways of doing things ... what would be best alternative to have to change less code in the future ?
Have the "clientId" in the Authorization: OAuth token (like access token)
Have JWT token in the Authorization header (looks too much - signing,base 64 etc for simple client id tracking ...)
Any ideas would be appreciated
We recently implemented this for one of our REST platforms and we used a combination of BOTH the points you mentioned, meaning Authorization header & JWT token. Although, JWT is ONLY for authentication and GETTING an access_token (oauth token) which is later used with calling actual resource apis. I will discuss how we handled this situation and you can decide on how you want to implement it.
1) Authentication
Client sends a JWT to your authentication service (/api/oauth2/auth). (If you want more reading on JWT, you can read here and here of how JWT is implemented by google and how you can use spring-security-jwt libary to handle all the signing and encrypting/decrypting). You get the "clientId" out of JWT after decrypting and verifying the signature and after server does all the authentication, you respond back with a 'refresh_token' and an 'access_token'. Server will save the access_token as well and map it to the clientId so that when client makes requests using access_token, you can know which client is making the request. The access_token expires in some time (ideally in an hour) and when it expires, the client uses the 'refresh_token' to get a new access token by posting refresh_token to some refresh token url (/api/oauth2/auth/token)
2) Authorization
Client takes the 'access_token' and uses the access token to make all the subsequent requests on all other apis (/api/*). Ideally, the access_token is sent as a part of the "Authorization" header. Server uses request filters (if you are using JAX-RS, you can use something like ContainerFilterRequest to add filters to specific url patterns and intercept them) to filter EACH request and parse out the Authorization header value. You will get the access_token from the header and from the access_token you can get the clientId that you mapped in step 1). You can do other authorization logic in the security filter and if everything goes through, you can use this information to LOG that clientId and the request that the client made.
This way you can kill 2 birds with one stone : Implement a security layer & log the information about customers (what calls they are making, how many time etc. etc.). In case you don't want to implement security filter just yet (as you mentioned it might be in the future), for now, the clients can just pass on the "clientId" (base64encoded or not, upto you) as a part of "Authorization" header. If all the calls are from a "trusted" network, it should be ok, although not as secure. This way, when you ACTUALLY implement a JWT and Oauth based security layer, all you have to do is change your ContainerFilterRequest logic to parse out access_token instead of client id (as mentioned in step # 2).
I hope this helps ! For more information on security filters you can have a look at this answer: Basic Authentication of a resource in Dropwizard. It says dropwizard, but it mostly talks about JAX-RS.
To implement full AuthN/AuthZ layer for consumer tracking would be an overkill for now.
We thought to use either to Authorzation header to pass custom client_id token:
Authorization: Custom <Client_Id>
or to use some limited version of JWT (no signatures as there no intent to validate them)
as access token
Authorization: JWT <JWT>
Where JWT could be:
{"alg":"none","typ":"JWT"}
{
"iss":"Client_ID",
"aud": REST Service URI,
"iat":1328550785
}
I do not see description of access_token format in the specification https://datatracker.ietf.org/doc/html/rfc6749#section-1.4
Are there any contraints to use JWT as access token?