Can someone explain how to implement token authentication in Flutter? I don't understand how this process works. Can someone tell me step by step or even show with examples how this happens? I use Swagger to implement.
Request URL:
https://api.dev.certihire.com/api/v1/sessions
Response body:
{
"_v": "1.0",
"data": {
"user": {
"id": 606,
"firstName": null,
"lastName": null,
"email": "lysohor.rostyslav.cr#gmail.com",
"phoneNumber": null,
"role": "User"
},
"token": {
"accessToken": "eyJhb....ms",
"refreshToken": "eyJhb....AI",
"expireDate": "2022-06-07T12:26:18Z",
"type": "Bearer"
}
}
}
Response headers:
api-supported-versions: 1.0
connection: keep-alive
content-type: application/json; charset=utf-8
date: Tue, 24 May 2022 12:26:18 GMT
server: nginx/1.12.2
strict-transport-security: max-age=31536000
transfer-encoding: chunked
x-content-type-options: nosniff
The process is quite simple, really. When you get your response body, retrieve both tokens (access and refresh) and the user ID and store them safely (e.g. using flutter_secure_storage)
When you re-enter the app, check if the tokens exist (at least the refresh token) and if yes, you can use the user ID to automatically log in. If there is no token, or the server replies with it not being valid (with a status code most likely equaling 401, but it depends), you should redirect the user to the login page for manual re-authentication.
Every request to a protected route should be made with the access token. Usually, the access token has a purposely short expiration date, so that only 1-2 resources can be used using this token. You can then use your refresh token to regen a token pair and retry the original request.
If you use JWT, the information about the expiration date is built into the token, so there is no need to send it. Client side, you should build your requests with the authentication type in mind, in your case using Bearer <'token'>. The logic to verify signed tokens is usually server-side, and the client's concern should only be storing them safely.
As a final note, HTTP(s) requests in Flutter can be done using the http package.
Use shared preferences to store the refresh and access token when the user has logged in. After that whenever the app starts check if there is any access or refresh token stored inside the shared preferences. If present then move to the main screen of the application otherwise move to the login screen. Also, do check if the stored refresh token is valid or if it's session has expired. In case expired, do log out and clear the shared preferences so that next time when app starts it will not find any token inside the shared preferences and the user will see the login screen again. Same with any logout button provided for log out for the user.
For shared preferences you need to add shared_preferences package inside your pubspec.yaml file.
Related
I'm using Keycloak with Google as identity provider. I need a refresh token from Google in order to manage the calendar of users. Here are my Keycloak Google IDP settings:
After login I fetch the refresh token according to https://www.keycloak.org/docs/latest/server_development/index.html#retrieving-external-idp-tokens. Which looks like this:
{
"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"expires_in": 3599,
"refresh_expires_in": 0,
"refresh_token": "YYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"token_type": "Bearer",
"id_token": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
"not-before-policy": 0,
"scope": "openid https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
"accessTokenExpiration": 1593706596
}
Now the problem is when I login a second time and then try to fetch the refresh token again it's gone:
{
"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"expires_in": 3599,
"refresh_expires_in": 0,
"token_type": "Bearer",
"id_token": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
"not-before-policy": 0,
"scope": "https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid",
"accessTokenExpiration": 1593706782
}
I'm really not sure how this is possible. One thing that came to my mind is that Keycloak is not respecting the "Request refresh token" setting on subsequest logins, but I don't know how to verify this.
It's not a Keycloak bug, it's a Google specification. The refresh_token is only provided on the first authorization from the user. Here the documentation of this behavior:
refresh_token: A token that you can use to obtain a new access token. Refresh tokens are valid until the user revokes access. Again, this field is only present in this response if you set the access_type parameter to offline in the initial request to Google's authorization server. - source
If you want to get the refresh token again you must:
Go to the page showing Apps with access to your account: https://myaccount.google.com/u/0/permissions.
Under the Third-party apps menu, choose your app.
Click Remove access and then click Ok to confirm
The next OAuth2 request you make will return a refresh_token (providing that it also includes the 'access_type=offline' query parameter.
related to this answer
I am working on integrating the UBER API into my app.
The first step goes fine : an Authorization url is created, the user is redirected to Uber, logs in, accepts to share the desired scope, then UBER redirects to my redirection url as provided in the dashboard and in the authorization url.
When then I make a POST request to
https://login.uber.com/oauth/v2/token
to obtain an access token
Here is the payload I send to Uber on this url
{
"code": "obtained_from_redirection_url",
"client_id": "XXXXXXX",
"client_secret": "XXXXXXX",
"redirect_uri": "https://myredirection-url.me",
"grant_type": "authorization_code",
"scope": "all_trips history history_lite places profile request request_receipt ride_widgets"
}
the response is always
{
"error": "unsupported_grant_type"
}
As you can see, the grant_type : authorization_code value is the one provided by Uber.
The scope you see here, is exactly the same as the one sent with the authorization url.
So We can at least say that the error shown does not correspond to what causes the problem, which remains a mystery to me.
I would appreciate any help.
Well the answer to this issue was that the token endpoint expects the POST request to be made with a Content-Type = x-www-form-urlencoded instead of JSON.
It is not documented in UBER's doc, and I wish it was cause it made me loose days and days seeking a solution.
I'm trying to make a Gogle Chrome extension that requires user authorization to a SugarCRM 7.5 instance with OAuth 2.0 and I need to store the access token that's retrieved. I may need more clarification on how launchWebAuthFlow works.
Firstly, I can retrieve an access token from SugarCRM by using a POST request (not a GET request) that returns an access and a refresh token.
When I tried using the code below I kept getting the error: "authorization url can not be loaded" and when I checked the background console (I already know that my auth URL is wrong). Any help is appreciated even if you don't know SugarCRM. Just a general answer that can get me started is much appreciated.
manifest.json
{
"name": "Auth Sample",
"version": "1",
"manifest_version": 2,
"minimum_chrome_version": "29",
"key": "<long key>",
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": [
"identity",
"https://<sugar instance>/*"
]
}
Background.js
chrome.identity.launchWebAuthFlow(
{'url': '<url-to-do-auth>', 'interactive': true},
function(redirect_url) { console.log(redirect_url); });
My URL is definitely not gonna work here but it's something like this:
var client_id = '<client id from Oauth 2.0>';
var redirectUri = chrome.identity.getRedirectURL("sugarcrm");
var url = "https://<sugar instance base url>/rest/v10/oauth2/token?client_id=" + client_id + "&callbackURL=" + redirectUri + "&response_type=token"
What's the url-to-do-auth (is this the URL to do a regular login (username and password) to SugarCRM or is it a GET request through the REST API to do login)
I don't have a UI web auth flow for my SugarCRM instance. Is it possible to create one from the application or should that be on the server?
I can only make POST requests to get an access token. Will that still work with launchWebAuthFlow?
Update
I ended up storing the token using the local chrome.storage api and only stored the refresh token which will keep getting a new access token every time the application is run(I think it's more secure than just storing the access token as that will force it to always keep changing without passing other credentials)
After more search on launchWebAuthFlow. I found out that the url launchWebAuthFlow takes as parameter will launch a web page(with interactive parameter set to true) which is hosted on the server, that will let the user login and will return an access token if success. This url is actually an endpoint on the restful server. You need to create this endpoint that will be a get request with some parameters including a callbackURL, client_id and response_type. Then it will respond with another function that will be a post request and will take the username and password. If credentials are correct, it will return the access token as a parameter in the callbackURL(chrome extension specific url that contains the extension ID).
Please feel free to correct me or add something if I'm wrong.
There's only thing that server has to do; just check any access token's validity.
Clients send to the server user id and access token obtained by FB.getLoginStatus. As I expected, there would be any URL that checks access token's validity, like http://xxx.facebook.com/access_token?=xxxxxxxxxxxxxxxxxxxxxxxxxxxx.
That returns whether it's available one or not or is there any API (server side) for that?
The officially supported method for this is:
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
See the check token docs for more information.
An example response is:
{
"data": {
"app_id": 138483919580948,
"application": "Social Cafe",
"expires_at": 1352419328,
"is_valid": true,
"issued_at": 1347235328,
"metadata": {
"sso": "iphone-safari"
},
"scopes": [
"email",
"publish_actions"
],
"user_id": 1207059
}
}
You can simply request https://graph.facebook.com/me?access_token=xxxxxxxxxxxxxxxxx if you get an error, the token is invalid. If you get a JSON object with an id property then it is valid.
Unfortunately this will only tell you if your token is valid, not if it came from your app.
Just wanted to let you know that up until today I was first obtaining an app access token (via GET request to Facebook), and then using the received token as the app-token-or-admin-token in:
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
However, I just realized a better way of doing this (with the added benefit of requiring one less GET request):
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app_id}|{app_secret}
As described in Facebook's documentation for Access Tokens here.
Simply request (HTTP GET):
https://graph.facebook.com/USER_ID/access_token=xxxxxxxxxxxxxxxxx
That's it.
The app token can be found from this url.
https://developers.facebook.com/tools/accesstoken
I found this official tool from facebook developer page, this page will you following information related to access token - App ID, Type, App-Scoped,User last installed this app via, Issued, Expires, Data Access Expires, Valid, Origin, Scopes.
Just need access token.
https://developers.facebook.com/tools/debug/accesstoken/
Exchange Access Token for Mobile Number and Country Code (Server Side OR Client Side)
You can get the mobile number with your access_token with this API https://graph.accountkit.com/v1.1/me/?access_token=xxxxxxxxxxxx. Maybe, once you have the mobile number and the id, you can work with it to verify the user with your server & database.
xxxxxxxxxx above is the Access Token
Example Response :
{
"id": "61940819992708",
"phone": {
"number": "+91XX82923912",
"country_prefix": "91",
"national_number": "XX82923912"
}
}
Exchange Auth Code for Access Token (Server Side)
If you have an Auth Code instead, you can first get the Access Token with this API - https://graph.accountkit.com/v1.1/access_token?grant_type=authorization_code&code=xxxxxxxxxx&access_token=AA|yyyyyyyyyy|zzzzzzzzzz
xxxxxxxxxx, yyyyyyyyyy and zzzzzzzzzz above are the Auth Code, App ID and App Secret respectively.
Example Response
{
"id": "619XX819992708",
"access_token": "EMAWdcsi711meGS2qQpNk4XBTwUBIDtqYAKoZBbBZAEZCZAXyWVbqvKUyKgDZBniZBFwKVyoVGHXnquCcikBqc9ROF2qAxLRrqBYAvXknwND3dhHU0iLZCRwBNHNlyQZD",
"token_refresh_interval_sec": XX92000
}
Note - This is preferred on the server-side since the API requires the APP Secret which is not meant to be shared for security reasons.
Good Luck.
I found this question, which has an answer, but facebook changed the token format since then, now it is something like:
AAACEdEose0cBACgUMGMCRi9qVbqO3u7mdATQzg[more funny letters]ig8b3uss9WrhGZBYjr20rnJu263BAZDZD
In short, you cannot infer anything from it.
I also found the access token debugger, which shows the information I am looking for if you paste a token in, which is nice, but does not help me do it programmatically.
Point is, if someone gets a token for a user, he can use it to access the graph, which is what I do in my application - I want to be sure that people are forwarding the token that was issued to them by my application, and not another.
My application flow is:
Get access token from facebook (nothing special, in the way it is described in here , Server-side Flow. (also iPhone and android and used, but they have similar flows if I recall correctly))
[device] <-> [facebook]
With that access token, the device will access my application server with the token
[device] <-> [Jonathan's application]
At my server I attach the access token to the user and use that to give permissions to that user in my application. (using the facebook connect to authenticate users)
My application is secured, and the access done is also authenticated regardless of facebook, BUT! in this flow, the a weak link I identified is that I cannot authenticate for sure that the access token I got was signed for my application - I do not like it because I cache the tokens for offline use, I want to be 100% sure they are for my application, with my permissions.
So what will be the (best) way to authenticate that the token I got is related to my application (for relation to user, I use the token to access /me and see which user this token is for)
I do not need to decrypt the token (i guess its some sort of AES), I am just looking for an endpoint that will tell me the token matched my application id.
(EDIT: Using the C# SDK, if it matters.. But a graph/rest call to give that info is just as good as well :) )
https://graph.facebook.com/app/?access_token=[user_access_token]
This will return the app this token was generated for, you can compare that against your app's id.
The official graph endpoint for inspecting access tokens is:
GET graph.facebook.com/debug_token?
input_token=[user_access_token]&
access_token=[app_token_or_admin_token]
Example response:
{
"data": {
"app_id": 138483919580948,
"application": "Social Cafe",
"expires_at": 1352419328,
"is_valid": true,
"issued_at": 1347235328,
"metadata": {
"sso": "iphone-safari"
},
"scopes": [
"email",
"publish_actions"
],
"user_id": 1207059
}
}
app_token_or_admin_token can be obtained using the Graph API call:
GET graph.facebook.com/oauth/access_token?
client_id={app-id}
&client_secret={app-secret}
&grant_type=client_credentials
The debug_token endpoint will fail if that user_access_token doesn't belong to the app that generated the app_token_or_admin_token.
Relevant facebook documentation:
Inspecting access tokens:
https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/#checktoken
App Tokens:
https://developers.facebook.com/docs/facebook-login/access-tokens/#apptokens
A documented way to ensure this is to use appsecret_proof.
GET graph.facebook.com/v2.5/me?access_token=[TOKEN]&appsecret_proof=[PROOF]
This verifies not only that it is a valid token, but also that the token belongs to the app. It also gets you user data in one go.
You can derive PROOF above in C# using this (from here):
public static string ComputeHmacSha256Hash(string valueToHash, string key)
{
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
byte[] valueBytes = Encoding.ASCII.GetBytes(valueToHash);
byte[] tokenBytes = new HMACSHA256(keyBytes).ComputeHash(valueBytes);
valueBytes = null;
keyBytes = null;
StringBuilder token = new StringBuilder();
foreach (byte b in tokenBytes)
{
token.AppendFormat("{0:x2}", b);
}
tokenBytes = null;
return token.ToString();
}
ComputeHmacSha256Hash(accessToken, appSecret);
Why not to use official way of doing things? Here's the request from FB's own video about security.
Request:
https://graph.facebook.com/debug_token?input_token={token-to-check}&access_token={app_id}|{app_secret}
Response:
"data": { "app_id": {token-app-id}, "user_id": {token-user-id}, ... }
Link to an official video: https://www.facebook.com/FacebookforDevelopers/videos/10152795636318553/
I made a screenshot so that time is visible, and you can find more info if you are interested.