I need to change the openid issuer
"issuer": "https://gate.domain.com:443/oauth2/token"
to this
"issuer": "https://api.domain.com",
Also change the JWT iss from
"iss": "wso2.org/products/am"
to
"iss": "https://api.domain.com"
I see in the api-manager.xml file a tokenEndPoint tag. I'm not sure altering that end point is the correct why to achieve what I want.
I read in this stackoverflow question, which has a similar issue. It said to alter this tag in identity.xml
<IDTokenIssuerID>${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token</IDTokenIssuerID>
I've tried altering in both places and my JWT iss claim still return "wso2.org/products/am"
Since I'm using a custom JWT Generator, I ended up just overwriting the iss with the static address I needed.
Related
I am requesting access token from Microsoft Graph using this procedure:
I request access to the following scopes:
User.Read.All openid profile email offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send
After the consent screen in web browser, the redirection occurs and the codes are sent to temporary localhost web server running on user's PC.
The code received is exchanged for access_token and refresh_token
When I try to query Microsoft Graph for user's profile I query:
GET https://graph.microsoft.com/v1.0/me
Header of the GET request contains:
Authorization: Bearer token-here-all-in-one-line
But I get the resulting JSON:
"InvalidAuthenticationToken"
"CompactToken parsing failed with error code: 8004920A"
I would normally assume the token is not correct, but I tested the same token from C++ app and a small PHP app, and I always test the same error. To be sure that it is not the wrong token, I deliberately modify it to a wrong token and then I get:
"CompactToken parsing failed with error code: 80049217"
After googling - 8004920A means "token rejected" (the error I have problem with) and 80049217 means "malformed token" so that is consistent with me deliberately inserting false data as token.
So I would assume that the token is correct but Microsoft Graph rejects it to query user profile information which is consented and approved.
I have tested the token on IMAP and SMTP access and there it works - mails are sent and received, so the access_token is definitely good.
Any ideas why Microsoft Graph rejects my attempt to query user profile?
Do I need to enable something when registering application in AzureAD portal?
I am doing this from C++ or from PHP so I don't think the code is of relevance here.
Not sure if this is the case, but it may be. You cannot use the same token for graph access and another API acccess (such as Outlook REST api, in your case). Meaning, you cannot mix scopes from different namespaces. Not sure why ("by design"?), you just can't. You need two separate tokens.
I faced similar issue with SharePoint REST api access (so may not be 100% sure it is also true for Outlook as well, but probably it is). I used a relatively easy way out - exchanging the token requested for openid profile email offline_access for token for https://outlook.office.com/... on my server (using the on_behalf_of flow).
If you don't really need the graph scope User.Read.All you could also try simply removing that one from your first authorization call that obtains the token.
As #Nikolay has stated, the tokens for Graph and Outlook can't be mixed. And Microsoft has designed it poorly so that IMAP.AccessAsUser.All for example can't be used for IMAP access - the access_token simply won't work unless the https://outlook.office.com/ prefix is added.
But - I found another way, which works for just reading the user's profile and email address which is by decoding the id_token. As Microsoft documentation states - openid profile and email scopes will work with both Graph and Outlook. profile token can be used to extract the information about user's profile.
As explained on:
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens
id_token contains information such as email address, name etc.
A sample ID token from their page is:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjFMVE16YWtpaGlSbGFfOHoyQkVKVlhlV01xbyJ9.eyJ2ZXIiOiIyLjAiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTEyMjA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkL3YyLjAiLCJzdWIiOiJBQUFBQUFBQUFBQUFBQUFBQUFBQUFJa3pxRlZyU2FTYUZIeTc4MmJidGFRIiwiYXVkIjoiNmNiMDQwMTgtYTNmNS00NmE3LWI5OTUtOTQwYzc4ZjVhZWYzIiwiZXhwIjoxNTM2MzYxNDExLCJpYXQiOjE1MzYyNzQ3MTEsIm5iZiI6MTUzNjI3NDcxMSwibmFtZSI6IkFiZSBMaW5jb2xuIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiQWJlTGlAbWljcm9zb2Z0LmNvbSIsIm9pZCI6IjAwMDAwMDAwLTAwMDAtMDAwMC02NmYzLTMzMzJlY2E3ZWE4MSIsInRpZCI6IjkxMjIwNDBkLTZjNjctNGM1Yi1iMTEyLTM2YTMwNGI2NmRhZCIsIm5vbmNlIjoiMTIzNTIzIiwiYWlvIjoiRGYyVVZYTDFpeCFsTUNXTVNPSkJjRmF0emNHZnZGR2hqS3Y4cTVnMHg3MzJkUjVNQjVCaXN2R1FPN1lXQnlqZDhpUURMcSFlR2JJRGFreXA1bW5PcmNkcUhlWVNubHRlcFFtUnA2QUlaOGpZIn0.1AFWW-Ck5nROwSlltm7GzZvDwUkqvhSQpm55TQsmVo9Y59cLhRXpvB8n-55HCr9Z6G_31_UbeUkoz612I2j_Sm9FFShSDDjoaLQr54CreGIJvjtmS3EkK9a7SJBbcpL1MpUtlfygow39tFjY7EVNW9plWUvRrTgVk7lYLprvfzw-CIqw3gHC-T7IK_m_xkr08INERBtaecwhTeN4chPC4W3jdmw_lIxzC48YoQ0dB1L9-ImX98Egypfrlbm0IBL5spFzL6JDZIRRJOu8vecJvj1mq-IUhGt0MacxX8jdxYLP-KUu2d9MbNKpCKJuZ7p8gwTL5B7NlUdh_dmSviPWrw
The token consists of 3 parts - header, payload and the signature. They are separated by a dot. That's a standard JWT (JSON Web Token) See details at - https://en.wikipedia.org/wiki/JSON_Web_Token
After separating them and then base64 decoding each (the signature doesn't give anything useful after decoding) the result is:
1) The header
{
"typ": "JWT",
"alg": "RS256",
"kid": "1LTMzakihiRla_8z2BEJVXeWMqo"
}
2) The payload
Payload which contains the user info. Additional information can be extracted by adding Optional claims when registering app in Azure AD - (Token configuration in the menu on the left).
{
"ver": "2.0",
"iss": "https://login.microsoftonline.com/9122040d-6c67-4c5b-b112-36a304b66dad/v2.0",
"sub": "AAAAAAAAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ",
"aud": "6cb04018-a3f5-46a7-b995-940c78f5aef3",
"exp": 1536361411,
"iat": 1536274711,
"nbf": 1536274711,
"name": "Abe Lincoln",
"preferred_username": "AbeLi#microsoft.com",
"oid": "00000000-0000-0000-66f3-3332eca7ea81",
"tid": "9122040d-6c67-4c5b-b112-36a304b66dad",
"nonce": "123523",
"aio": "Df2UVXL1ix!lMCWMSOJBcFatzcGfvFGhjKv8q5g0x732dR5MB5BisvGQO7YWByjd8iQDLq!eGbIDakyp5mnOrcdqHeYSnltepQmRp6AIZ8jY"
}
3) The signature
For verification purposes only.
1AFWW-Ck5nROwSlltm7GzZvDwUkqvhSQpm55TQsmVo9Y59cLhRXpvB8n-55HCr9Z6G_31_UbeUkoz612I2j_Sm9FFShSDDjoaLQr54CreGIJvjtmS3EkK9a7SJBbcpL1MpUtlfygow39tFjY7EVNW9plWUvRrTgVk7lYLprvfzw-CIqw3gHC-T7IK_m_xkr08INERBtaecwhTeN4chPC4W3jdmw_lIxzC48YoQ0dB1L9-ImX98Egypfrlbm0IBL5spFzL6JDZIRRJOu8vecJvj1mq-IUhGt0MacxX8jdxYLP-KUu2d9MbNKpCKJuZ7p8gwTL5B7NlUdh_dmSviPWrw
I would like to test Google Indexing API with Postman.
I have already configured the api correctly on https://console.cloud.google.com/ for using it on a wordpress site with the plugin Instant indexing and everything works ok.
Now I simply want to do a POST call with Postman, in particular I did not found anything clear about the type of authentication (Oauth 2) to use for the call.
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"method": "google.indexing.v3.UrlService.PublishUrlNotification",
"service": "indexing.googleapis.com"
}
}
]
}
}
Anyone can provide me an example? Thank you.
As stated in the comments, Google API's normally use OAuth2. Some exceptions might use an API key, but even then most of those still limit the data you can access with an API key versus what you can access using OAuth2
This particular API does indeed use OAuth2 as per the docs. This means you need an access token (as the error message states), so you will need to generate one.
Easiest would be to use a library for one of the many programming languages, as per the docs here.
I'll give a simple example in python as that is my preferred language. The following code uses the google-auth library (docs). It's the base library to deal with credential objects. You will need to download the JSON representation for the service account you created. See the relevant docs. Note that you should handle this file as if it were a password, same as with the access token itself, although the access token has a default lifetime of 3600 seconds, AKA 1h.
If you get an error message that the token is expired, create a new one. Read up on refresh tokens for that, but that's beyond the scope of this question.
Code was tested on a virtualenv for python 3.10
requirements.txt
google-auth
requests
main.py
from google.oauth2 import service_account
import google.auth.transport.requests
# create credentials object
creds= service_account.Credentials.from_service_account_file("/path/to/serviceaccount.json")
# we need a scoped credentials object as per
# https://developers.google.com/search/apis/indexing-api/v3/prereqs#requirements
scoped_creds = creds.with_scopes(['https://www.googleapis.com/auth/indexing'])
# create a request object for the refresh method
request = google.auth.transport.requests.Request()
# make the library do it's magic and get a token
scoped_creds.refresh(request)
print(scoped_creds.token)
This will print out an access token. (if it prints with dots (....) at the end, remove them.
Another easy option would be to (ab)use the gcloud command line tool. There's a couple of steps to this to get to work. You can use the cloud shell as an easy way to do this.
Download or copy the serviceaccount JSON file I mentioned above.
Activate the serviceaccount in gcloud using:
gcloud auth activate-service-account SERVICE_ACCOUNT#DOMAIN.COM --key-file=/path/key.json
Print the access token with this command:
gcloud auth print-access-token
The hard and masochistic way would be to use something like CURL or HTTPS requests manually to generate the token. Feel free to do so, but I'm just going to point you to the docs for that. It's a bit of a pain in the posterior.
You can test the token as explained in this answer.
Now that you have the access token, you can use it in POSTMAN by setting it in the header for the call. See this nice answer, but basically add the following request header key/value pair, replacing TOKEN with the generated token.
KEY: Authorization
VALUE: Bearer TOKEN
For anyone interested I managed to make it work by following these steps:
To obtain the authentication token with Postman first go to https://console.cloud.google.com/apis/credentials and in your web application ID client set https://oauth.pstmn.io/v1/callback as Authorized redirect URIs
Now in Postman Application in Authorization tab select OAuth 2.0 and configure the fields according to your client_secret.json (image)
Now click on 'Get new access Token' and you should get it
Specify the url and type as raw json body (image)
I called this revisited since there are existing questions under the same title. These indicate that the problem described would be solved starting from IS V5.3.
However I am using IS V5.7.0 and still have the problem that my userinfo call only returns something like {"sub":"Peter"} or an email address if so configured as " Subject Claim URI" under my SP.
Whatever I do with "local claim" settings seems to have no effect in this scenario.
In short it is a complete mystery to me on how to acquire more than a single attribute value from an open id connect call to ws02 IS.
All help appreciated
Thanks Peter
There was an issue already reported for versions below IS 5.3.0 and it was fixed with IS5.3.0 onwards. Please check the Jira here. I was able to invoke the userinfo endpoint in IS5.7.0 and able to get the claims successfully.
Sample request:
curl --location --request GET 'https://localhost:9443/oauth2/userinfo?scope=openid' \
--header 'Authorization: Bearer 7ca4671f-ab55-33b2-978e-b0795dacc1ef'
So please verify the following things.
Response:
{
"country": "SL",
"sub": "piraveena",
"email": "piraveena#wso2.com"
}
So please verify the following things.
Check whether you have configured those claims under claim configuration in the service provider.
Only the claims listed under openid scope will be returned from userinfo endpoint. Also please make sure that those claims are added into openid scope. In the management console, Navigate to Manage > OIDC Scopes > List > Openid and check whether the requested claim from the SP is listed under the openid scope. Please check this documentation for configuring adding claims in openid scope
Also verify whether the authenticated user has those relevant attributes in his user profile
Also please check whether you were able to get the claims in id_token. If you have not configured the above steps properly, then you won't be able to get those claims in id_token as well.
I want to change the expiration time of JWT token created in WSO2 Api Manager.
It seems that the default time is 15 minutes.
{"typ":"JWT","alg":"none"}.
{
"iss": "wso2.org/products/am",
"exp": 1464255150998,
...
}
From this link - Wso2 Jira - APIMANAGER-3493 , it seems that we can use "SecurityContextTTL" property in /repository/conf/api-manager.xml under APIKeyManager to change the expiration time of the JWT.
But I could not find this setting available in api-manager.xml (not even as a commented out configuration). I am using wso2am-1.9.1.
Is this property removed from the configurations? If yes, then how can we change the expiration time of the JWT?
Is there any documentation available mentioning about this property and its usage?
Any pointers are appreciated. Thanks.
It should be applied as follows, under APIKeyValidator section,
<APIKeyValidator>
<SecurityContextTTL>5</SecurityContextTTL>
</APIKeyValidator>
But as per the Jira you mentioned, it seems there is a bug in this version.
WSO2 has fixed this in next version(2.0.0), using a new property, see [1].
[1] https://wso2.org/jira/browse/APIMANAGER-4575
In my Rails app, I am trying to set a cookie to be picked up by Ember Simple Auth's cookie store after the page has loaded. I am using the Ember Simple Auth OAuth2 authorizer.
Right now, I am just planting the OAuth data directly as the cookie value:
{
"token_type": "bearer",
"access_token": "3ec78864cc017982fdeeb0c092bfbea3f104df1e18c9c67f222581d9353f3fce",
"refresh_token": "cb03c07b8845ea7b40251b0df46839177bd7b51b3dd1d23f167890b9e1549f07",
"created_at": 1436454055,
"expires_in": 7060,
"expires_at": 1436461254
}
I'm guessing this isn't what Ember Simple Auth expects because the syncData function reads it once and then replaces it with this value after the next cookie poll:
{ secure: {} }
What should the data look like for OAuth 2? I'm guessing it's the same no matter how it's stored (cookie vs. local storage vs. ephemeral storage).
After looking at this screenshot from this post, I figure I'm probably way off, and I've been having trouble understanding where to poke around in the Ember Simple Auth source to figure this out.
Ember Simple Auth only uses the cookie to store its internal state. The cookie cannot be set from the server and also should not be used on the server side. The library is solely meant for implementing token authentication for stateless (= cookie-less) APIs.
See the README for more info about how OAuth 2.0 works with ESA: https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-oauth2#ember-simple-auth-oauth-20
I believe that Marco's advice in the accepted answer should be followed if at all possible.
But, poking around a little more, I figured out that the cookie content would need to look like this in order for Ember Simple Auth OAuth 2 to recognize it:
{
"secure": {
"authenticator": "simple-auth-authenticator:oauth2-password-grant",
"token_type": "bearer",
"access_token": "3ec78864cc017982fdeeb0c092bfbea3f104df1e18c9c67f222581d9353f3fce",
"refresh_token": "cb03c07b8845ea7b40251b0df46839177bd7b51b3dd1d23f167890b9e1549f07",
"created_at": 1436454055,
"expires_in": 7060,
"expires_at": 1436461254
}
}
Of course, there are some drawbacks to this approach, namely that upgrading Ember Simple Auth could break if it changes the format of how it stores this data.
If you set cookies from another app like I'm attempting to do, you'd need to be mindful about reviewing this format after each update of Ember Simple Auth. The best way to accomplish this is to create a blank Ember app with Simple Auth installed and configured, then review the format of the data that it stores after you sign in to the app.