I am working on an eCommerce Website and an App. We use SAP Hybris for OAuth 2.0.
To get an access token I send a Cliend ID, Client secret, Username and Password to the auth server.
Problem Example:
If I log in with the App first and then the Website, I won't be able to refresh my token in one of the sessions.
The token I receive from the server is pretty standard and looks like this:
{
"access_token":"9T7IziRSIM_QIqFtttM8rhf83zU",
"token_type":"bearer",
"refresh_token":"MztkOmh67gIEiMwX5sED-Rug51c",
"expires_in":43199,
"scope":"basic"
}
The only difference is that in the "Website Token" the expires_in would have a lower value than 43199 since it was requested after the "App Token".
Since both the access_token as well as the refresh_token are identical, the moment one of them expire and we try to fetch a new token the first session that does it will receive completely different credentials. As soon as the second session (which is now expired) tries to also refresh it's credentials the server will deny new credentials since the old credentials can be used only once to get new tokens.
Every 12 hours the tokens become expired and the first client to request a new token effectively logs out the other client by doing so.
Question:
What could I do to deal with this problem?
I was thinking it should be possible to send a unique ID to my request to generate a unique token. However I cannot find any information about this on the SAP Docs.
I'm having issues understanding the whole process of authenticating a client to consume my API built on Laravel. Some things just don't click for me right now.
I'm trying to implement an API and an OAuth server both on Laravel. The API will be consumed by a native mobile app that is trusted. The flow that makes more sense to me is "Password grand token" as described in the Laravel's Passport docs: https://laravel.com/docs/7.x/passport#password-grant-tokens
As i understand the implementation:
User installs my mobile app.
Upon installation, he's prompted with the "enter username/password" to continue to use the app
Upon hitting submit, i make a POST request to my Laravel oAuth server implementation on "/oauth/token" with "grant_type", "client_id", "username", "password", "scope". I'm leaving out the "client_secret" because i understand that it's not a good idea to store the secret on the client device.
The server then checks the already created( `php artisan passport:client --password` ) "client_id", "username", "password" and "response_type"
If all matches, it generates a token, and responds with "acces_token" & "refresh_token"
I can make now make calls to my API's endpoints "/api/whatever/method_name"
My issue is at point 4. I can only issue the access token if the user already exists in my database, but i'm assuming it's the first time the user uses my app. postman_response
Do i also need an "authentification" step, in witch the user sends username/password and the OAuth server prompts the "authorize app" to use your data, and at this point to save the user in the database and only then proceed?
Usually you have an register route, that is without authorization else you have no entry into the application. Imagine your routes file.
Route::middleware('auth:api')->group(function () {
Route::post('/todos', 'TodoController#index');
});
// Without auth
Route::post('/register', 'RegisterController#register');
For hiding credentials, it is often easier to do a proxy approach, so you backend can hold client_id and client_secret, since it will always be the same (unless you are building an oauth server).
Route::post('/login', 'LoginController#login');
Which will receive username and password, internally call oauth/token and add client_id and client_secret in the process and return the token. To save some calls through the signup, you can do the same approach after you have registered, get the oauth token, with the credentials you have at registrering and return the token imediatly.
I would recommend the following:
In log in method, check if user exists.
If exists, do log him in.
else, first register him up, and then log him in
lastly, return access token
Using the OktaSignIn widget, I see I can get res.session.token. Can I use this (or some other attribute) in another app -- with the APIKey -- and validate that this is a valid session?
We just want a simple to use auth system and don't want to set up OpenAuth...
Can't seem to find any APIs that do what I need.. but could have missed it of course...
Edit. Basically... our front end uses the OktaSignInWidget... then we want to use this in a Bearer token our API Services layer can validate.
Thanks!
Looks like this will work...
/api/v1/sessions/me
Get id from this.
{"id":"102wtHeHhr4Q4q4rh2Fjy6pGA","userId":"00u9uwkfyfiz3Y7uk0h7",
Then... this can be passed and using the API key...Issue a GET to...
/api/v1/sessions/102wtHeHhr4Q4q4rh2Fjy6pGA
Returns...
Session...
The call to /api/v1/sessions requires the API key -- which is fine.
As you mentioned, you can use the session id to see if the session is still valid on the Okta server by:
Exchanging sessionToken for okta session
After redirecting back to your app, calling /api/v1/sessions/me to get the sessionId
Using that sessionId in the request to /api/v1/sessions/id with an apiToken to see if it's still valid
This will exist as long as the user has not logged out of Okta, but the browser state might be different - for example, the Okta session cookie will normally be deleted when the user's browser closes, while the session might still exist on the server.
Alternatively, to check if the browser session still exists, you could make the validation check on the client side by making the request to /api/v1/sessions/me - the one gotcha is to make sure that CORS is enabled for both the domains your apps are running on so they have permissions to make this request to Okta.
The above methods work, but it does sound like what you should be looking into is Okta's API Access Management (OAuth2) - it was designed for this type of flow (passing Bearer tokens to your API services layer).
Using the provided environment and collections from Okta in Postman, I run "Primary Authentication with Trusted Application" and Authenticate with no issues. I then copy and set my SessionToken. When I try to then "Create Session with Session Token" it always fails with:
{
"errorCode": "E0000004",
"errorSummary": "Authentication failed",
"errorLink": "E0000004",
"errorId": "oaeDAJpYgLNQS6AnhFwQ3QLcA",
"errorCauses": []
}
Is there something I am doing wrong? The example Postman call for "Create Session with Session Token" only shows the SessionToken being passed in. I shouldn't need to authenticate again?
Help is appreciated.
It looks like you are doing the right thing here, but please note that you can only call the "Create Session with Session Token" endpoint once. If you call it twice (or more), you will end up with the E000004 error you mentioned.
If you are positive that you only tried this one, instead of copying the sessionToken value you get back from the Primary Authentication call (you can try that one too instead of "Primary Authentication with Trusted Application") into the sessionToken variable, try to copy and paste it directly into the Body of the "Create Session with Session Token" field by replacing the {{sessionToken}} placeholder with your session token value.
Please let me know if that helps!
Removing Postman and starting fresh and following http://developer.okta.com/docs/api/getting_started/api_test_client.html worked for me.
The error code "E0000004" is returned in case of following possible conditions:
Locked out accounts
Invalid credentials
Access denied by a sign-on policy
The errorSummary will be "Authentication failed" only to prevent brute force attacks by not providing much information.
Usually, Google OAuth2.0 mechanism is working great.
The user confirms permission to access Google account with selected scopes.
The refresh token is retrieved and saved to long time storage.
Each time needed (if the access token expired) access token is retrieved and used to access APIs.
But sometimes (thus far only two times for more than 6 months) I've experienced strange behaviour:
Requests to Google APIs return Invalid Credentials (401) error.
Refreshing the access token (using the stored refresh token) does not help.
Here is some structured output I've got when testing this issue:
+ ------------------------------------------------------------------------- +
| 1.TRYING TO REFRESH THE TOKEN. |
| 2.DONE REFRESHING THE TOKEN. |
+ ------------------------------------------------------------------------- +
| access: **************************************************** |
| refresh: ********************************************* |
| expires: 3600 |
| created: 2013-07-23 13:12:36 |
+ ------------------------------------------------------------------------- +
I've also tried to verify the "fresh" access token by sending requests to
https://www.googleapis.com/oauth2/v1/tokeninfo
+ ------------------------------------------------------------------------- +
| 1. TRYING TO CHECK THE TOKEN . |
| 2. DONE CHECKING THE TOKEN THE TOKEN. |
+ ------------------------------------------------------------------------- +
| issued_to: ************.apps.googleusercontent.com |
| audience: ************.apps.googleusercontent.com |
| user_id: ************ |
| expires_in: 3600 |
| email: **********#gmail.com |
| verified_email: 1 |
| access_type: offline |
| scopes:: |
+ ------------------------------------------------------------------------- +
| https://www.googleapis.com/auth/userinfo.email |
| https://www.googleapis.com/auth/userinfo.profile |
| https://www.googleapis.com/auth/plus.me |
| https://www.googleapis.com/auth/drive |
+ ------------------------------------------------------------------------- +
But when I try to access drive feed the response is:
Error calling GET https://www.googleapis.com/drive/v2/files (401) Invalid Credentials
domain: global
reason: authError
message: Invalid Credentials
locationType: header
location: Authorization
We also experienced the same issue with calendars.
So:
Token was valid before (everything worked).
Refreshing token still works.
Requesting a feed responds with "Invalid Credentials" error.
All the other tokens are still working great, meaning that the code is valid.
Normally when the token is revoked "invalid_grant" error is returned when trying to refresh the token.
Questions
What can be the reason for this behaviour? If the refresh token was revoked or got invalid in some other way, should the request for new access token produce error?
Is there a way to validate the refresh token?
Per the Google API docs on errors & error codes:
https://developers.google.com/drive/handle-errors#401_invalid_credentials
401: Invalid Credentials
Invalid authorization header. The access token you're using is either expired or invalid.
error: {
errors: [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization",
}
],
"code": 401,
"message": "Invalid Credentials"
}
}
This matches your version of the error exactly, and so is very probably what Google thinks is wrong with your request.
But, as you well know, Google API requests can return errors that are distinctly unhelpful to actually diagnosing the problem. I have gotten "Invalid Credentials" errors for a number of reasons. It is almost always really because I have made some sort of change that I thought would not matter, but really does.
My first thought (shot in the dark here) would be to go to the Google API console:
https://code.google.com/apis/console
Googles auth token verifier ( https://www.googleapis.com/oauth2/v1/tokeninfo ) can return a valid response, but maybe the client secret or client id will have been changed.
Even tiny changes in the response body can also cause this error.
I don't know how you are making requests, whether by REST calls or a client lib, but I use the ruby lib which allows a command line interface to making API calls. I have found this & the OAuth2 Playground very helpful in diagnosing Google API calls.
Just an FYI: I have only gotten 2 errors from the Google API: "Invalid Credentials" and "Insufficient Permissions". The latter has almost always had to do with bad scopes. The former is just about everything else.
I would also say that if you have only experienced 2 errors in 6 months, you are lucky!
I had this problem when I tried experimenting with changing the redirect url in google console and then updating my json credentials file on server. I had to clear the session variables before starting afresh. So in your project just do this once:
session_start(); //starts a session
session_unset(); //flushes out all the contents previously set
Remember to remove the session_unset() after dry running it once.
I'm on Development environment. I had this problem too.
First I tried refreshing the credentials. No result. Then I deleted my app (since I'm still on development enviroment, that was ok, but BE CAREFUL WITH THIS ACTION if you're already using this on production), created a new one, updated the credentials JSON on the client... still, no result.
I solved it by opening on a new browser instance which wasn't logged in my Google Account (Private Browsing, since I'm on Firefox), logged on my Google Account once again, and tried using my client (which is a Web Application). I was redirected to the authorization screen as expected and after that, it worked fine for me.
Maybe this behavior is due to a limitation which Google describes as follows:
There is currently a limit of 50 refresh tokens per user account per client. If the limit is reached, creating a new token automatically invalidates the oldest token without warning. This limit does not apply to service accounts.
There is also a larger limit on the total number of tokens a user account or service account can have across all clients. Most normal users won't exceed this limit but a developer's test account might.
I recently experienced this weird error. My fix: I put the function that unsets all of the sessions before redirecting to AuthUrl.
clearing storage in Google Chrome worked for me (don't know all the details of what 'Clear storage' is clearing):
F12 (Ctrl+Shift+I)
Application Tab
Clear storage
I had the same problem with this error:
The redirect URI in the request, does not match the ones authorized for the OAuth client.
But found this very simple solution by abhishek77in here:
https://coderwall.com/p/fmr5ag/avoid-invalid-credentials-with-google-oauth2
The solution is:
If you are using
https://github.com/zquestz/omniauth-google-oauth2 make sure to
follow the note in README. "You must enable the "Contacts API" and
"Google+ API" via the Google API console."
Enabling these in the Google API console fixed the "Invalid credentials" problem for me.
userInfo: Invalid Credentials
I got the following error because the scopes array elements i was trying to access ie profile and email whose links I got from google+ api scope page: came to be somehow false/invalid so I went to my consent screen and there
under Scopes for Google APIs was mentioned email profile openID on hovering on each I got there respective urls replacing my old ones with these resolved my error
I received (401) Invalid Credentials when I removed the access to my Google Account for the particular app. So what I had to do was to request the authorization URL (the one which starts with https://accounts.google.com/o/oauth2/auth), again.
I ran into this same problem when I needed to change my scopes from Read Only to Read And Write All Files. So, I updated my scopes from at the top of my file from Read Only to:
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/drive'];
Google, from their API guide, has these comments that say whenever you change scopes, you must update credentials. I believe this means, although I am not certain, that the token must be updated. The old token is still held by Google and it thought that I only had Read Only access, hence why it would return a 401 error. So, I need to remake my token, but Google never offered a new consent screen that would allow me to say allow Read And Write To All Files. So, I needed to get that screen to come up again, so it would create a new token to replace the old one:
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
getNewToken(oauth2Client, callback);
// oauth2Client.credentials = JSON.parse(token);
// callback(oauth2Client);
}
});
Since I already had a saved token, it was never creating a new one. So, I just commented out the using of the old token and told it to get a new token, no matter if we have one or not. Then, I went to my Connected Apps in Google and deleted my old connecting credential. I'm not sure if this step is necessary, but I am only trying to access my personal account. Then, when I ran my program, it prompted me to re-authenticate, and everything worked and I did not receive an authentication error. Once done, make sure to remove the commented out lines for using already made tokens. I was using the Google API quickstart.js file for all of this.
So, when I updated my scopes, the old token was still using the Read Only scope, therefore I would get (401) Invalid Credentials.
I resolved this problem when I removed files json in c:\Users\[user]\.credentials.
Maybe this is helpful to someone:
I had a similar issue using the JavaScript Google-API client for Calendar API. At random times it would work but mostly I got the same error. Adding scopes, testing key, nothing helped. After a few hours I found this solution, no idea why it works but it solved the issue for me:
gapi.client.init({
'apiKey': API_KEY, <-- DOESN'T WORK
'clientId': CLIENT_ID,
'discoveryDocs': DISCOVERY_URLS,
'scope': SCOPE
}).then(function() {
// gapi.client.setApiKey(API_KEY); <-- ADD THIS
})
If you're using an account that's part of a GSuite set up, you might need to add GSuite Basic to the account. You get to this by Users > Click on user > Licenses.