I have an authorization server and my client is an angular app.I'm not a third party app.
I used this symfony bundle https://github.com/trikoder/oauth2-bundle
I'm using grant type password.
{ "grant_type":"password", "client_id":"myclientid", "username":"john#doe.com", "password":"foo", "client_secret":"3d4a940.....3c1ea38b5" }
And the response is :
{ "token_type": "Bearer", "expires_in": 60, "access_token": "eyJ0eXAi....nK0Ag", "refresh_token": "def50200dfce4da3....fdc689e5" }
The access_token is only valid for 1 min, afterward the client need to use the refresh token to be able to talk to my api :
{ "grant_type":"refresh_token", "client_id":"myclientid", "client_secret":"3d4a940.....3c1ea38b5" "refresh_token": "def50200dfce4da3....fdc689e5" }
client_id and client_secret are stored in a table.
My question is :
Is it safe/recommended to store the client_id and the client_secret in the local storage of the front angular app ? Because it basically represents the user credentials and if some one steal them they would have access to the api. But without them the client can't send request to the api.
I crawled the web but I can't find a real answer, even on the oauth 2 documentation
Thanks
A few points here:
Your angular app should use the authorization code flow + open id connect
Consider using a more standard authorization server and more standard access token lifetimes
Session storage has fewer issues around Safari / Firefox and SPA restarts
Whether to use session storage for tokens depends on data sensitivity + risk analysis
Consider that the session time for which access tokens are valid sometimes maps to a user consent
I have a write-up, which is based around trade offs between usability and security: https://authguidance.com/2019/09/08/ui-token-management/
Related
I got SAML setup on my AD and I have the private and certificate PEMs along with the certificate and metadata and I want to know if it's possible to bypass the login from API? I'm developing a Node.JS API which I want to make requests to the server behind the SAML login but I need to bypass it with what I got.
Maybe a way to generate a token from Azure's API (to use as bearer authorization) would work like how you do it with OAuth?
I'm not asking for any other solution (like whitelisting etc.), I just need it to be token / API based
To bypass azure ad , you may chose for the On behalf flow.
For single-page apps (SPAs), here we pass an access token to a middle-tier confidential client to perform OBO flows instead.
This will only work, if the respective permissions (scope grants) are
already granted when you try to get an access token using the
on-behalf of flow.
Here API A authenticates to the Microsoft identity platform token
issuance endpoint and requests a token to access API B.
https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token
with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
client_id=xxx
&client_secret=saxx1s
&scope=https://graph.microsoft.com/user.read+offline_access
&requested_token_use=on_behalf_of
Here we need to expose api and here I am giving user.read
And exposed api
The response has access token , refresh token
{
"token_type": "Bearer",
"scope": "https://graph.microsoft.com/user.read",
"expires_in": 3269,
"ext_expires_in": 0,
"access_token": "xx",
"refresh_token": "xxxx"
}
Please check Microsoft identity platform and OAuth2.0 On-Behalf-Of flow - Microsoft Entra | Microsoft Learn
Reference : how-to-use-azure-ad-access-token-to-bypass-microsoft-online-login
I have this Authorization request that works.
How can I replicate it in Python?
I am using an Azure AD to authenticate the access.
Since you are working with python, your case is a : Oauth2 login for SSR web applications with Microsoft
Goal
Get an access_token from interactive login using the oauth2 authorization code grant
Steps
Here I will list all the steps required to do it with any language
Create a web with session with at least these endpoints
/ : home page
/callback : server route or path able to receive query params like /callback?code=123456. This along with your base domain will be called redirect_uri. Sample : http://localhost:8080/callback or http://acme.com/callback
Create and configure an app in Azure Dev Console. Same process is in Google, Facebook, Linkedin, etc. As a result you should have a clientId, clientSecret and a redirect url
Create a simple web with classic session in which if user is not logged-in, redirect (302) to this url:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=foo&response_type=code&redirect_uri=foo&response_mode=query&scope=offline_access%20user.read%20mail.read
clientid and redirect_uri are important here and should be the same of previous step
After that, browser should redirect the user to the platform login
If user enters valid credentials and accepts the consent warning, Microsoft will perform another redirect (302) to the provided redirect_uri but with special value: The auth code
http://acme.com/callback?code=123456798
In the backend of /callback get the code and send it to this new endpoint
Add a client_id & client_secret parameters
Add a code parameter with the code sent by microsoft
Add a redirect_uri parameter with previously used and registered on azure. Sample http://acme.com/callback or http://localhost:8080/callback
Add a grant_type parameter with a value of authorization_code
Issue the HTTP POST request with content-type: application/x-www-form-urlencoded
You should get a response with the precious access_token:
{
token_type: 'Bearer',
scope: 'Mail.Read User.Read profile openid email',
expires_in: 5020,
ext_expires_in: 5020,
access_token: 'eyJ0oVlKhZHsvMhRydQ',
refresh_token: 's_Rcrqf6xMaWcPHJxRFwCQFkL_qUYqBLM71UN6'
}
You could do with this token, whatever you configured in azure. Sample: If you want to access to user calendar, profile, etc on behalf of the user, you should have registered this in the azure console. So the clientid is related to that and human user will be prompted with something like this
Libraries
There is some libraries provided by microsoft (c#, nodejs) which will save you a little work. Anyway the previous explanation are very detailed.
Advice
Read about oauth2 spec: https://oauth.net/2/
Read about oauth2 authorization code flow login before the implementation with python
https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2
https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow
https://github.com/msusdev/microsoft_identity_platform_dev/blob/main/presentations/auth_users_msalnet.md
Check this to understand how configure the azure web console: https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
Check my gist https://gist.github.com/jrichardsz/5b8ba730978fce7a7c585007d3fd06b4
I am working on a project to let a client authorize their google ads account, and then use those authorized credentials to download data on their behalf. I have a webapp that successfully Authorizes the app to do things on the clients behalf. This generates an access code that I then trade for two credentials, an access token and a refresh token. This refresh token then gets passed to a database, where a separate app attempts to query the googleAds API.
It is my understanding that the Google Oauth engine only needs the refresh token.
I am trying to authorize by use of load_from_dict() or load_from_env() methods of the GoogleAdsClient class. Both yield the same error: google.auth.exceptions.RefreshError: ('invalid_client: Unauthorized', {'error': 'invalid_client', 'error_description': 'Unauthorized'})
I have verified the developer_token, client_id, and client_secret are all accurate to what is in the API console. I have also verified the refresh_token is being passed correctly to the credential dict.
I am really at a loss on where to go from here. I have read many stack overflow threads with similar titles, and yet I am still stuck at the same place.
Here are some relevant links.
Google Ads API configuration
Google Identity and Server side web apps
Google's example of an API call
Relevant code
class GoogleAds:
def __init__(self):
self.scope = ['https://www.googleapis.com/auth/adwords']
self.client_id = os.getenv('GOOGLE_ADS_CLIENT_ID')
self.client_secret = os.getenv('GOOGLE_ADS_CLIENT_SECRET')
self.developer_token = os.getenv('GOOGLE_ADS_DEVELOPER_TOKEN')
self.refresh_token = os.getenv('GOOGLE_ADS_REFRESH_TOKEN')
def authorize(self):
credentials = {
"developer_token": self.developer_token,
"refresh_token": self.refresh_token,
"client_id": self.client_id,
"client_secret": self.client_secret,
"use_proto_plus":"True",
"grant_type": "refresh_token",
}
print(credentials)
googleads_client = GoogleAdsClient.load_from_dict(credentials)
service = googleads_client.get_service("GoogleAdsService")
request = googleads_client.get_type("SearchGoogleAdsRequest")
return service, request
'error': 'invalid_client', 'error_description': 'Unauthorized' Can be a very hard error to debug. It can mean a number of things.
Currently it Implies to me that the user has not authorized this client.
First ensure that the refresh token has not expired. Second ensure that the client id and client secrete used to create the refresh token are the same one that you are using to request a new access token.
oauth2#expiration
I ended up refreshing the Client_Secret in the google API client and that seemed to have gotten me through.
Q: It is outside the scope of this question, but is it possible to get that value from the authorization step?
A: You can get the customer IDs you have access to with the client.get_service("CustomerService") method. There is also a way to get account hierarchy. I will probably be using (Frankensteining) that to move forward
I am building a mern application.
the backend built using express exposes an api which users can create data and access the data they have created.
I want to allow users to sign in with google and get authorization to create and access the resources on this api which i control (not on google apis).
I keep coming across oauth 2 / open id connect articles stating that an Id token is for use by a client and a access token provided by a resource server should be used to get access to an api.
e.g. https://auth0.com/blog/why-should-use-accesstokens-to-secure-an-api/
the reason stated for this is that the aud property on the id token wont be correct if used on the api.
I realise that some sources say: that if the spa and api are served from same server and have same client id and therefore audience I can use and id token to authenticate to the api, but I am looking to understand what I can do when this is not the case?
I feel using oauth2 for authorization is overkill for my app and I cant find any information about how to use open id connect to authenticate to my api.
Surely when you sign in to Auth0 authourization server using google it is just requesting an open id connect id token from google?
I am wondering if using Authorization Code Grant flow to receive an id token on the api server would allow me to authenticate a user to my api?
in this case would the api server be the client as far as open id connect is concerned and therefore the aud value would be correct?
I can generate an url to visit the google oauth server using the node googleapis library like so:
const { google } = require("googleapis");
const oauth2Client = new google.auth.OAuth2(
'clientid','clientsecret',
"http://localhost:3000/oauthcallback",//this is where the react app is served from
);
const calendar = google.calendar({ version: "v3", auth: oauth2Client });
const scopes = ["openid"];
const url = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: "offline",
// If you only need one scope you can pass it as a string
scope: scopes,
});
async function getUrl(req, res) {
console.log(url)
res.status(200).json({
url,
});
}
and use the following flow.
You are not supposed to access any API's using the ID-Token. First of all the life-time of the ID-token is very short, typically like 5 minutes.
You should always use the access-token to access API's and you can using the refresh token get new access-tokens. The ID-token you can only get one time and you use that to create the local user and local cookie session.
If you are using a SPA application, you should also consider using the BFF pattern, to avoid using any tokens in the SPA-Application
see The BFF Pattern (Backend for Frontend): An Introduction
I agree with one of the commenters that you should follow the principle of separation of concern and keep the authorization server as a separate service. Otherwise it will be a pin to debug and troubleshoot when it does not work.
I have a angular client app and a .net web api server.
I'm trying to understand how to implement security in the best way using tokens.
I thought about few options, and I don't know which one is the best, or if there is a better way.
JWT with expiration
1.User login with credentials -> Server returns a JWT with expiration (for example 60 minutes from login time). Each valid request to the server within this 60 minutes the server returns a new JWT token with a new expiration of 60 minutes. If user didn't send server request for 60 minutes he must login again.
This solution is very similiar to sessions.
Oauth2 - I don't think I understand this protocol correctly, so I apologize if what I'm saying is not correct.
2.User login with credentials -> user gets a refresh_token and access_token.
For every request, the client attaches the access_token. If server returns 401 (unauthorized) the client uses the refresh_token to create a new access_token and re-send the failed request with the new token.
The problem in this flow, for me, is that I don't know if I got unauthorized because the token was invalid or the user tried to access unauthorized resources.
This led me to a third solution.
3.User login with credentials -> user gets a refresh_token, access_token and access_token_expiration. When the user wants to create a request to the server, he checks if access_token has expired. If expired, the client will request a new access_token with new expiration and only then perfroms the request.
And 2 more little question about auth2:
1.Why do I have a refresh_token and access_token? Both of them are stored in client local storage. Why not to always use the refresh_token? For the server it makes sense to have a refresh_token and access_token since the refresh_token is secured.
2.Does the refresh token have an expiration date?
If it does, how can I create a new refresh token?
If it doesn't, isn't it a little unsave to give the ability to stay connected forever?
Thank you very much for your help
Option 3, login with credentials and getting an access token, refresh token and expiration time is the usual way.
eg.:
{
"access_token": "eyJ0eXA....CqVJcc",
"token_type": "bearer",
"expires_in": 3599,
"refresh_token": "AQAAAN...H2JXjIUAQ"
}
the expiration time of the JWT access token can also be found in the token itself in the payload field exp, in form of a UNIX timestamp:
{
...
"exp": 1500547257,
"nbf": 1500543657
...
}
With that information you can implement your own mechanism to check if your access token is still valid and refresh it when necessary.
The refresh token usually also expires. When the refresh token is expired, you need to start again with the credentials.
Additionally you can read this for further information about the topic: https://auth0.com/learn/refresh-tokens/
And here is a tutorial that talks about handling of refresh tokens in Angular:
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/