How to restrict a JWT / OpenID token to specific IP / CIDR - oauth-2.0

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"]
}

Related

Cas 6.1.x OAuth client_credentials with scope

It does not appear that I can setup scope in a service config for grant_type of client_credentials.
Is this possible? When requesting a token, I do get back an empty "scope" value. The only way I can get a value to appear is if I pass a query parameter of &scope=foobar. But this does not make sense that the client application is setting the scope.
I want to grant a token with permission to read from API1 and write to API2 but not read/write to API3. It seems I should be able to have a config as scope: [ "java.util.HashSet", [ "api1_read", "api2_write" ] ] basic on clientId config on the cas authorization server.
Then I would image that the resource service, when validating the token would also get a list of scopes allowed.
What am I missing?
You are not missing anything. This capability does not exist and could possibly be added to CAS 6.3 assuming time and sponsorship would be available. Support for scopes are only available as of this writing for OpenID Connect. For OAuth, they would need to be added to the codebase and released.

How to use JWT token issued by authentication server to allow access to another app?

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.

Is it possible to retrieve an OAuth2 access token from Google with client secret file of Google Apps Script project?

I want to reuse the OAuth2 client-secret of a Google Apps script project to access Google APIs on behalf of this script (e.g. via Sheets API reading a spreadsheet where the script has access). Users with a Google account granted the necessary scopes to the script. Now I'd like to replace the script with a new app without asking the users again for user consent. Typically, when the script (or the app) runs the users would be offline.
My first question would be, if this scenario is a supported OAuth2 use-case with Google API authorization, and if so, what would be the best way to implement it, especially to prevent security issues?
Client secrets of the script
OAuth2 client-secret file of the script from Google API Console, under Credentials. Also see Credentials, access, security, and identity and Setting up OAuth 2.0
The client-secrets.json looks like this:
{"web":{
"client_id": "57...1t.apps.googleusercontent.com",
"project_id": "project-id-95...70",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "K2...y1",
"redirect_uris": ["https://script.google.com/oauthcallback"]
}}
The file would be deployed with the app code (App Engine). But an alternate location such as Cloud Storage would be possible.
OAuth2 access token
In absence of the user, I would like to use the client ID and secret with the same scopes that were granted to the script originally, for getting an access token from the Google authorization server, something like this:
HTTP 200 OK
Content-type: application/json
{
"access_token": "987tghjkiu6trfghjuytrghj",
"scope": "foo bar",
"token_type": "Bearer"
}
I would like to use the access token in the HTTP Bearer header for the requests to the Sheets API on behalf of the old script.
Client_credentials request to authorization server
My (limited) understanding is, that I can use the grant-type client_credentials to get the access token from the authorization server. The request would look like this:
POST /o/oauth2/token
Host: https://accounts.google.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Authorization: Basic Base_64_string
grant_type=client_credentials&
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fspreadsheets
Where the Basic HTTP authorization is client_id:client_secret values, separated by a colon, and base64 encoded.
If I ditch grant_type or scope in the body, I will get corresponding errors.
The version as above resulted in: {\n "error" : "invalid_request"\n} response, no specifics provided. I have tried with client_id and client_secret in the body in combination with and without the Authorization header, all with the same error.
First Off let me start by saying that i am not an expert in app script or sheets i have used both a few times but i dont consider myself an expert in the subject.
When you authenticate someone their authentication is granted based upon the client id from within a project. They are granting you access to their data and approving the credential request. Think of it as a recored in Googles database someplace.
User 321 has granted consent to client 123 to access their sheets data.
So you have a project Super Script App which has client id 123 you are asking for access to edit sheets. If i approve it i am giving Super Script App with client id 123 access to my sheets. While i am sitting at my machine your app will run accessing my data. Now when i log off Super Script App with client id 123 cant access my data unless you have offline access and have a refresh token. With that refresh token you will be able to access my data when i am not there by requesting a new access token.
Now you want to make a new app. If you take the client id 123 and use it in your new app I am going to have to login to my google account but it will not popup and request that i give you permissions to access my data I have already granted client id 123 access to my sheets. Unless you have a refresh token your not going to be able to access this data without me being there.
If at anytime you ask for an extra scope I am going to have to approve that.
Now comes the fun part. I haven't actually tried this in a while. If you go to Google Developer console and create client id 321 under the same project as client id 123, and then use that in your new Super Script App V2 will i still have to grant it permission to access my data? I am going to lean towards probably not. Note a refresh token created with client id 123 will not work with client id 321 they are locked to a client unless its mobile and there is some magic their.
I am not exactly sure what you are doing with your second app so i hope this helps clear things up.

Possible reasons for invalid_token from AWS Cognito on https://api.amazon.com/auth/o2/tokeninfo

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.

How to identify a user authenticated with ADAL against ADFS 3.0?

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

Resources