I have ADAL for iOS working with an ADFS 3.0 server. It brings up a web view, the user authenticates and I get a call back with an access token.
The problem I have is that I get no information back about the user's identity. The userInformation property is nil. There's no id_token in any of the HTTP responses and I'm not sure how to request one in the first place. And I've seen elsewhere the Microsoft folks say that ADFS 3.0 doesn't support id_token at all. I've also parsed the JWT formatted access token and there's no information in there either that I can use to identify the user.
On the ADFS 3.0 server side, we have configured and enabled a Claim Rule that says to provide the relying party with GUID, Given Name, Surname, and Email Address. But adding that rule made no difference in the responses I get through ADAL.
How can I identify a user (i.e. get a GUID, first name, last name and email address) who was authenticated via ADAL against an ADFS 3.0 server? Is there an endpoint on the ADFS 3.0 server that I can hit with the provided access token where I can request this information?
It turns out that ADFS 3.0 may not support id_token, but if you have the Claim Rules for the Relying Party set correctly they will be added to the top level of the access token you receive. Apparently the claim rule should look something like this when it is correct:
The access token is a JWT token so it can be decoded and the values retrieved from it there. When decoded, it will look something like this:
{
"appid": "5f9a5589-6064-423a-8a1a-6a0d7ddda19f",
"aud": "x-msauth-glazersapp://com.example.MyApp",
"auth_time": "2016-08-08T22:32:14.459Z",
"authmethod": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"email": "me#example.com",
"exp": 1470699134,
"family_name": "John",
"given_name": "Doe",
"iat": 1470695534,
"iss": "http://adfs.example.com/adfs/services/trust",
"objectGUID": "c8oMVOOEskutnPVno41Y1w==",
"ver": "1.0",
}
"email", "family_name", "given_name" and "objectGUID" were added to the access token. And watch out, the GUID when provided like this is actually Base64 encoded.
More information available here: http://chrisrisner.com/Accessing-Resources-Secured-By-Azure-Active-Directory-with-iOS-and-Android
Related
I am getting the following output from the /userinfo endpoint
{
"sub": "XXXX#gmail.com",
"aud": [
"XXXXXXXXX"
],
"nbf": 1646097620.000000000,
"scope": [
"openid",
"profile",
"email"
],
"iss": "https://XXXXXX/authServer",
"exp": 1646097920.000000000,
"iat": 1646097620.000000000
I need the username and the email to be visible.
Could any one help me on this ?
We are working on reference documentation, including how-to guides for cases such as this one. See How-to: Customize the OpenID Connect 1.0 UserInfo response #537, and feel free to up-vote that issue if it would be helpful to you.
In the meantime, check out the configuration in OidcUserInfoTests which demonstrates how to customize the claims returned by the User Info endpoint. For example, you can simply map all of the claims from the JWT (access token), or you can map only specific claims.
You may also be interested in seeing the DefaultOidcUserInfoMapper. The default strategy used is to map the standard claims from the id_token that are resolvable by the granted OIDC scope(s). Since in your example, you have profile and email, you can also provide an OAuth2TokenCustomizer to add the associated claims to the id_token when it is created, and they will automatically show up in the User Info endpoint. See OidcTests for an example of customizing the id_token.
I have app A ( Java API backend) as authentication server based on username and password. After successful login, the authentication server returns below JSON response:
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYWNjb3VudC1yZXNvdXJjZSJdLCJ1c2VyX25hbWUiOiJzZWNvbmRfbGV2ZWxfbWFuYWdlckBjb21wYW55LmNvbSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE1MzMwMTk1ODksImF1dGhvcml0aWVzIjpbIlNFQ09ORF9MRVZFTF9NQU5BR0VSIl0sImp0aSI6ImZiYWJjZDM3LTc3OTEtNGU5YS1hNDg3LTU1YjI5ZDJhMDZhMiIsImNsaWVudF9pZCI6ImNybS1mcm9udGVuZCJ9.CdIe1xtDJhgk5px3uIfAS9cvabMNox9Pa7KUEc5qka4","token_type":"bearer","refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYWNjb3VudC1yZXNvdXJjZSJdLCJ1c2VyX25hbWUiOiJzZWNvbmRfbGV2ZWxfbWFuYWdlckBjb21wYW55LmNvbSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiJmYmFiY2QzNy03NzkxLTRlOWEtYTQ4Ny01NWIyOWQyYTA2YTIiLCJleHAiOjE1MzM2MjA3ODksImF1dGhvcml0aWVzIjpbIlNFQ09ORF9MRVZFTF9NQU5BR0VSIl0sImp0aSI6IjM5OTAwNDZjLThmOWMtNDMzNi1hOTlmLTFiMjIzZjAyMjcwNyIsImNsaWVudF9pZCI6ImNybS1mcm9udGVuZCJ9.V8cn5x7OefgJUwF68abxHCF8cB0axZf1edRGGnd4wkY","expires_in":3599,"scope":"read
write","jti":"fbabcd37-7791-4e9a-a487-55b29d2a06a2"}
The login form is in VueJS frontend app B. After successful login, the user is authorized to access protected resources (lets say view Bar objects, perform CRUD operations on Bar objects). The backend API for these Bar objects is another app C which is NodeJS app. So app A and app C backends running on separate servers and ports.
How can I protect (ie. allow only successfully logged in user) to accesss and manipulate Bar objects using above access_token and jti and expiration?
One way is to check for expiration time before allowing access to protected resources each time protected resource URL endpoints are touched. But is this correct approach and hack proof?
You can read the authentication header from the incoming message and base64 decode the first half of the JWT to get your user details and the expiry. I've done that using jwt.io and you get
{
"aud": [
"account-resource"
],
"user_name": "second_level_manager#company.com",
"scope": [
"read",
"write"
],
"exp": 1533019589,
"authorities": [
"SECOND_LEVEL_MANAGER"
],
"jti": "fbabcd37-7791-4e9a-a487-55b29d2a06a2",
"client_id": "crm-frontend"
}
But to trust that this hasn't been tampered with you need to check the signature. I normally use passport with node https://www.npmjs.com/package/passport-jwt
You probably defined the 256 bit secret when you encoded the JWT to begin with
Checking the signature makes sure the the base64 encoded part which is not securely encoded has not been altered.
I believe I am missing something with the implicit grant process and access tokens in aws cognito.
To this point:
Have a user pool, with a client app configured for implicit flow and scopes openid, profile, aws.cognito.signin.user.admin
Used a stack overview and the official documentation and older white papers to achieve:
Login process that redirects to aws cognito UI, and back to my app, with tokens and other information in the fragment portion of the URL.
The access_token value parses at jwt.io and signature checks out using the aws jwt tool
Problem:
The recommended step is to "verify that the access token belongs to us" through the tokeninfo api call.
When I attempt to call tokeninfo via javascript code and testing via postman (using: https://api.amazon.com/auth/o2/tokeninfo?access_token=eyJraWQiOiJoVFBa... )
I get the result:
{
"error_description": "The request has an invalid parameter : access_token",
"error": "invalid_token"
}
and an http header:
x-amzn-errortype: InvalidTokenException:http://internal.amazon.com/coral/com.amazon.panda/
Variants I have tried:
I have tried calls directly to the user profile (using Authorization header, and query string and x-amz-access-token header).
I have tried adjust parameter names (error becomes "access_token required" or something like that
I have tried adjusting scopes in the user pool
I have tried adding resource servers (though I am not there yet...)
The redirect after login looks like this:
https://staging.example.com/loginresult.html#id_token=eyJraWQiO<tokenremoved>&access_token=eyJraWQiOiJoVFBa<tokenremoved>&expires_in=3600&token_type=Bearer&state=whateverdevwants
The parsed values of the token (through jwt.io) are:
{
"sub": "5510a27d-ebcb-4883-8680-a66fd0462279",
"token_use": "access",
"scope": "aws.cognito.signin.user.admin openid profile",
"iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_OF5OOfdx0",
"exp": 1519352461,
"iat": 1519348861,
"version": 2,
"jti": "31950a91-e2a5-4060-8c31-977f49802d35",
"client_id": "3iuhnprmod0josge24ogarecfp",
"username": "5510a27d-ebcb-4883-8680-a66fd0462279"
}
Update: As answered below: just don't do this, it is conflating jwt tokens from cognito with whatever "Login With Amazon" was using.
In the example you refer to from Amazon they encode the access token using urllib.quote_plus for example in their PHP example.
Make sure you are URL encoding the access token too in your javascript code with encodeURI.
Also an error may be returned if the token has expired so make sure you verify a newly-minted token. Expiry is 3600 seconds - so make sure the token is less than an hour old.
EDIT
Looks like the documentation for Cognito is very different from the LWA (login with amazon) auth flow. The tokens in the examples you linked to aren't even JWT tokens!
The Cognito documentation here explains how to verify the JWT token.
Checkout the Using ID Tokens and Access Tokens in your Web APIs paragraph.
I create Backend server, which gets the ID Token from mobile application (iOS). How can I verify that this token is OK and can be used it securely?
Official Google's documentation about validating token:
https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken
It recommends to verify the ID Token locally, without sending verification request to the Google. Is it OK to check some fields from ID Token locally like in documentation or maybe should I send some request to Google to verify token as well?
Google documentation mentions about debugging and verifying ID Token with:
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
But it doesn't recommend to use it in production. I thought also about using Access Token along with the Id Token and verify Access Token first with:
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=
But does it make the whole process of validating client's credentials (mobile app, web app) more secure?
Fist let me start by saying I don't work for Google. However I have been developing with Google Oauth2 since 2012. A while back I asked a Googler just this question.
His recommendation was if you have a refresh token just request a new access token. If its bad the server will return an error. If you have an access token send a request if its bad the server will return an error.
There isn't really much point in validating it first your just sending two requests to the server for every request you make. All you will be doing is preventing errors on a small percentage of the requests you are making in the long run.
I have never bothered with the id token. Id token is a jwt so you should be able to open it I think.
update
You should consult Verifiy the integrity of the id token.
You can also do some checking on your own. The id token is a jwt if you decrypt it you get or by calling the tokeninfo endpoint
{
"iss": "https://accounts.google.com",
"azp": "407408718192.apps.googleusercontent.com",
"aud": "407408718192.apps.googleusercontent.com",
"sub": "11720055326",
"at_hash": "HQVaIRLqmsjaTt8KoOIQ",
"name": "Linda Lawton",
"picture": "https://lh3.googleusercontent.com/a-/AAuE7mDuIWqXzrrp-65cIhXSD2HjCI8WYsWHR0fDx5_wQPY=s96-c",
"given_name": "Linda",
"family_name": "Lawton",
"locale": "en",
"iat": 1567751,
"exp": 1567755
}
iss should be https://accounts.google.com
aud will be the client id of your app 7408718192.apps.googleusercontent.com
at_hash there may also be some way to validate against this but i haven't bothered
I'm developing an OpenID connect/JWT auth provider and part of the module is to allow the client to get a token and use it only with a specific IP address (if the client requests so).
I was thinking to make use of the scopes (i.e. to basically prefix them with the IP address and use an internal function to encode/decode it like 127.0.0.1:::getEmail) but if there is any standard I would definitely like to use it rather than to butcher the scopes.
Is there any other field that I can use to store this information? (e.g. azp).
Nothing prevent you from creating a new claim set in your JWT.
When the JWT is issued by your provider and if the client requested it, one (or more) IP address(es) could be added into your JWT payload.
Then, the resource server will take this claim into account.
Example:
{
"exp": 123456789
"iss": "Provider"
"aud": "Resource Server"
"sub": "My Client"
"ips": ["127.0.0.1","192.168.0.0/24"]
}