I have got a token key which contains the logged in person email address as well as the name and other end points.This was actually used in xero API connection.
scope = 'offline_access accounting.reports.read accounting.settings.read openid profile email'
I need to decode this token key and get the logged in email address and the name of the person who is logged in.
For an example my token key is as below.
b9b73c12b40a3bc1441f5bda331c4d7c64c0394956d5105eec61a71de19f8153
How can I decode this opaque Access Token and get the relevant information using python.
Clients should never decode access tokens directly, as jps says. You have these options:
READ USER FIELDS FROM ID TOKEN
The UI reads this JWT directly. An id token always has JWT format and is designed to be read by clients.
USE USER INFO ENDPOINT
The UI can send the access token to the User Info endpoint, using the message from step 24 of the above blog post.
GET USER INFO FROM API
This tends to be the most extensible option, since you can return any info you want, and you are not limited to what is contained in access tokens.
const token = req.headers.authorization.split(" ")[1]; //Bearer +token
const decodedToken = jwt.verify(token, "secret_message_long_string");
req.userData = { email: decodedToken.email, userId: decodedToken.userId };
This example is for decoding data for nodejs.
try the same way in python. Importing jwt and using the method verfiy passing the token and secret string as an argument.
Related
I have an MSAL app that creates authentication tokens for accessing various Microsoft APIs.
I provide the app specific scopes, and it creates a corresponding authentication token bearing those scopes. This app works perfectly fine for all types of endpoint I tried up
def _create_or_get_msal_app_client(
self, client_id: str, tenant_id: str | None = None, is_confidential_client: bool = False
) -> msal.ClientApplication:
"""
Create public or confidential msal app client to generate tokens
:param client_id: the client id (also known as application id)
:param tenant_id: the tenant id to use as authority, if not provided will use common authority
:return: the msal app
"""
if self._msal_app:
return self._msal_app
try:
authority = tenant_id if tenant_id else "common"
authority_url = f"https://login.microsoftonline.com/{authority}"
if is_confidential_client:
self._msal_app = msal.ConfidentialClientApplication(
client_id=[client_id], client_credential=[client_credential], authority=authority_url
)
else:
self._msal_app = msal.PublicClientApplication(client_id=client_id, authority=authority_url)
return self._msal_app
msal_app = self._create_or_get_msal_app_client(
client_id=[client_id], tenant_id=[tenant_id]
)
return msal_app.acquire_token_by_username_password(
username=[username], password=[password], scopes=[some scopes]
)
The tokens produced if inputted into jwt.io, will be marked as invalid, which is not a bad thing in itself, as noted by this qustion.
My problem is, when I try to call APIs with endpoints of type:
https://admin.powerplatform.microsoft.com/api/*
It almost seems like those kinds of endpoints has a different authorization system than the rest of the endpoints; For once, the token this EP uses in the UI I tool it from have a perfectly valid signature when trying to decode it in JTW.io, as opposed to the token issues by MSAL. But, this means that now I get in the response a 401 response when I try to use the MSAL-issues tokens, and the reason for the failed request, is, according to the response header resp.headers._store['www-authenticate'][1] is:
Bearer error="invalid_token", error_description="The signature is invalid"
This doesn't happen in any other Microsoft API I tried to call; for example in EPs of type https://graph.microsoft.com/v1.0/* the token produced by MSAL works perfectly fine.
The prime suspect in these types of authentication errors is the scopes asked. But no matter what scopes I ask, whether I ask for insufficient or sufficient or no scopes at all, I still get the same error.
Except what was suggested here to try to ask for the scope [client_id]/.defualt (where client id is the client id) but when I try to do that I get the error:
Bearer error="invalid_token", error_description="The audience \'[client_id]\' is invalid"
in the response headers.
I have another clue about what might be the problem in this forum, where the one asking the question mentioned that the EP is using OAuth. could it be that this is different from MS Graph in any way?
So my question is, how do I configure my MSAL app to work with https://admin.powerplatform.microsoft.com/api/*? Or alternatively, what EP could I use instead that does work with MSAL, and contains the same functionality as this one?
Note: looking at the headers in the request to get the tokens in the UI, I see they are using msal.js.browser, so this should be possible in theory. (by the way, the requested scope in the UI is [client_id]/.defualt openid profile offline_access) to the EP https://login.microsoftonline.com/common/oauth2/v2.0/token). When trying to decode the UI token in jwt.ms it says that the token is issued by AAD.
Example of a concrete EP I am trying to access: https://admin.powerplatform.microsoft.com/api/Environments/{env_name}/roleassignments/environmentadmin. The API is taken from the Power Platform Admin Center. More info about it here.
Goal:
I have a cloud function that impersonates a service account based on the service account email provided to it. I then want to use the impersonated service account to sign a jwt.
What I tried:
I have set up the impersonation of the service account like this:
credentials = google.auth.impersonated_credentials.Credentials(
source_credentials=SOURCE_CREDENTIALS,
target_principal=SA_EMAIL,
target_scopes=TARGET_SCOPES,
lifetime=300)
This brings me to a sub question: What target scopes do I need to specify?
After creating the impersonated credentials, I try to use signer (docs) to sign the payload of the JWT:
payload = {
'iat': now,
"exp": now + expiry_length,
[...]
}
signer = target_credentials.signer
jwt = google.auth.jwt.encode(signer, payload)
The error message I receive is:
AttributeError: 'Credentials' object has no attribute 'key_id'
Question:
I suspect I am not on the right track with my approach using signer.
How is it possible to achieve my goal?
In your example code, you have only create the payload section. A signed JWT also requires a header.
The header looks like this:
header = {
'kid': pkey_id, # Signing Private Key ID
"alg": "RS256", # Google uses SHA256withRSA
"typ": "JWT"
}
The header and payload are base64 encoded and then concatenated with a dot (period symbol) in between. You sign the combined data.
The problem that you will have is that you need to private key id (pkey_id in my example). That value is located in the service account JSON key file. However, Google does not publish the JSON key file that is used for signing. The key used is private to Google. You can get the private key by signing some data first as the pkey_id is returned as part of the response. Then sign your JWT.
I recommend that you switch from using the Sign Blob API and use the SignJwt API instead. The SignJwt API does not require that you create a JWT header.
IAM Service Account Credentials API
Method: projects.serviceAccounts.signJwt
This is the code I use to retrieve idToken in my flutter app
Future<void> _handleSignIn() async {
try {
final result = await _googleSignIn.signIn();
final ggAuth = await result!.authentication;
print(ggAuth.idToken); // this is the one that I use as token value
print(ggAuth.accessToken);
} catch (error) {
print(error);
}
}
And this is the code I use to access user info in my backend.
from google.oauth2 import id_token
from google.auth.transport import requests
try:
idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
userid = idinfo['sub']
print(userid)
except ValueError:
print('Invalid token)
But after replacing the token variable with the token received from flutter app and replacing the CLIENT_ID with the id generated for my app (available in the console) , it still throws the error of invalid token. What am I doing wrong?
EDIT- When I use https://jwt.io/ to decode the token it works as expected and I get all the details.
Authentication Think of the id token as your birth certificate. It just identifies you as a person.
Authorization Think of the access token as your drivers license its what contains the permissions that you have to drive a car.
An Id token does not out write give you permission to access any data on google servers. With an access token you have been authorized to access some data in this case some profile information.
The issue you are having is that you are using sign-in in. Sign in is Open id connect think of it as a birth certificate (Id token). There is a user their who is signing in to your app. They are in fount of their computer. By default with Google signin gives you get basic profile information. What you are doing with Jwt.io is checking the claims that are returned in the Id token. Id tokens are just the token that allows your application to identify that the user is logged in. There should also be a sub claim there you should use this id to map the user to your integral user system. There may be a user name and email address in the claims but you can not guarantee that they will be there every time google has stated they do not always return these claims.
To get user profile information after the user has logged in you should be using the people api the people.get method when passed the access token will return to you the profile information of the currently authenticated user.
beyond that you should IMO not be passing tokens to your backend if you want the token in the backend you should log the user in using your backend language.
Okay so the idToken that I had was correct but flutter terminal was not able to print its full length and because of which I was copying only half the token and was getting a signature error when checking manually.
I have added Firebase to allow clients to authenticate directly from the web app client (browser). I am using the firebase-web JS package and it works great. I can see in my browser that I receive a user object with information about the user, including an idToken.
I need to then authenticate this user on my server backend, which is python django. In the Firebase docs I found a how-to for exactly what I am trying to do, which is to verify the id token.
Since they don't have the supported Firebase sdk for python, I need to use a third party solution. I have come to the python-jose package after finding it listed on the jwt.io site. The example looks simple enough:
jwt.decode(token, 'secret', algorithms=['RS256'])
This is my first time using JWT. I don't know what to use for the 'secret'. I tried pasting my id token as token, and the web API key from the Firebase console for secret, but got this error:
jose.exceptions.JWKError: RSA key format is not supported
I also tried the JWT debugger, which seems to be reading most of my id token correctly, but the signature verification is looking for a public and/or a private keys, which like the 'secret' are escaping me.
I am really at a loss for how to find this secret, and how to verify the JWT id token in general. The information on the Firebase docs (third-party section) is:
Finally, ensure that the ID token was signed by the private key
corresponding to the token's kid claim. Grab the public key from
https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com
and use a JWT library to verify the signature. Use the value of
max-age in the Cache-Control header of the response from that endpoint
to know when to refresh the public keys.
I have tried pasting the whole json blob from that googleapis url into the JWT debugger, but still getting an "invalid signature" alert. I don't understand how to use that public key.
Should python-jose work for this approach? If so, what should I use for the secret? If not, can someone point me in the right direction?
Thanks.
I finally found the answer I was looking for in this post: Migrating Python backend from Gitkit to to Firebase-Auth with python-jose for token verification
Since the time of the post there have been updates made to the python-jose package, which gives better support for firebase id tokens. Here is some working code ( jose version 1.3.1 ) on how to use python to decode the firebase id token:
import urllib, json
from jose import jwt
idtoken = "<id token passed to server from firebase auth>"
target_audience = "<firebase app id>"
certificate_url = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com'
response = urllib.urlopen(certificate_url)
certs = response.read()
certs = json.loads(certs)
#will throw error if not valid
user = jwt.decode(idtoken, certs, algorithms='RS256', audience=target_audience)
print user
I am trying to fetch captions from YouTube video using YouTube Data API (v3)
https://developers.google.com/youtube/v3/guides/implementation/captions
So, first I tried to retrieve a captions list using this url:
https://www.googleapis.com/youtube/v3/captions?part=snippet&videoId=KK9bwTlAvgo&key={My API KEY}
I could retrieve the caption id that I'd like to download (jEDP-pmNCIqoB8QGlXWQf4Rh3faalD_l) from the above link.
Then, I followed this instruction to download the caption:
https://developers.google.com/youtube/v3/docs/captions/download
However, even though I input the caption id and my api key correctly, it shows "Login Required" error.
I suppose I need OAuth authentication, but what I am trying to do is not related to my users's account, but simply downloading public caption data automatically.
My question is: Is there any way to process OAuth authentication just once to get an access token of my own YouTube account and then reuse it whenever I need it in my application?
I can't speak to the permissions needed for the captions API in particular, but in general, yes, you can OAuth to your app once using your own account and use the access and refresh tokens to make subsequent OAuth'd requests to the API. You can find the details of generating tokens here:
https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps#Obtaining_Access_Tokens
To perform the steps manually (fortunately, you only need to do this once):
If access has already been granted for an app, it needs to be removed so that new auth credentials can be established. Go to https://security.google.com/settings/security/permissions (while logged into your account) and remove access to the app. If the client ID or secret change (or you need to create one), find them at https://console.developers.google.com under API Manager.
To grant access and receive a temporary code, enter this URL in a browser:
https://accounts.google.com/o/oauth2/auth?
client_id=<client_id>&
redirect_uri=http://www.google.com&
scope=https://www.googleapis.com/auth/youtube.force-ssl&
response_type=code&
access_type=offline&
approval_prompt=force
Follow the prompt to grant access to the app.
This will redirect to google.com with a code parameter (e.g.,
https://www.google.com/?code=4/ux5gNj-_mIu4DOD_gNZdjX9EtOFf&gws_rd=ssl#). Save the code.
Send a POST request (e.g., via Postman Chrome plugin) to https://accounts.google.com/o/oauth2/token with the following in the request body:
code=<code>&
client_id=<client_id>&
client_secret=<client_secret>&
redirect_uri=http://www.google.com&
grant_type=authorization_code
The response will contain both an access token and refresh token. Save both, but particularly the refresh token (because the access token will expire in 1 hour).
You can then use the access token to send an OAuth'd request manually, following one of the options here, essentially:
curl -H "Authorization: Bearer ACCESS_TOKEN" https://www.googleapis.com/youtube/v3/captions/<id>
or
curl https://www.googleapis.com/youtube/v3/captions/<id>?access_token=ACCESS_TOKEN
(When I tried the second option for captions, however, I got the message: "The OAuth token was received in the query string, which this API forbids for response formats other than JSON or XML. If possible, try sending the OAuth token in the Authorization header instead.")
You can also use the refresh token in your code to create the credential needed when building your YouTube object. In Java, this looks like the following:
String clientId = <your client ID>
String clientSecret = <your client secret>
String refreshToken = <refresh token>
HttpTransport transport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(transport)
.setJsonFactory(jsonFactory)
.setClientSecrets(clientId, clientSecret)
.build()
.setRefreshToken(refreshToken);
try {
credential.refreshToken();
} catch (IOException e) {
e.printStackTrace();
}
youtube = new YouTube.Builder(transport, jsonFactory, credential).build();
I imagine you can do something similar in Python with the API Client Libraries, although I haven't tried Python.