I am trying to build an iOS application with a custom token caching mechanism. I authenticate to FB on the device and I store the FB access token on my server against a session token on my server and then make FB API requests from the server.
The FB Login works fine. I am able to cache the token onto my server. But the real problem happens when I run the app the next time after I logged into FB.
This is the flow I follow to check if my app is logged into FB.
1) Hit a url on my server to get the FBAccessToken for a given session token for my application. This is an asynchronous request.
2) The asynchronous request callback is fired. If a token is present, cache it in my custom FBSessionTokenCachingStrategy class.
3) In the callback I initialize a FBSession object using:
FBSession *session = [[FBSession alloc] initWithAppID:nil permissions:#[#"basic_info"] urlSchemeSuffix:nil tokenCacheStrategy:self.tokenCache];
Then, I check the session.state. If I get a FBSessionCreatedTokenLoaded, then I decide to proceed with the openBehaviour method. However, I get a FBSessionStateCreated instead of FBSessionStateCreatedTokenLoaded even when a token has been cached on the server and the fetchFBAccessTokenData returns a FBAccessTokenData object.
Wonder why this happens?
Here is my implementation of the fetchFBAccessTokenData method of my custom FBSessionTokenCachingStrategy class:
-(FBAccessTokenData*) fetchFBAccessTokenData {
NSLog(#"Fetching FB Access token");
if (self.fbTokenData != nil) {
NSLog(#"Found FB Token");
FBAccessTokenData *fbTokenData = [FBAccessTokenData createTokenFromString: [self.fbTokenData objectForKey:#"fb_token"] permissions:nil expirationDate:nil loginType:1 refreshDate:nil];
NSLog(#"%#",fbTokenData.accessToken);
return fbTokenData;
}
return nil;
}
It returns an FBAccessTokenData object but I still get an incorrect session state.
Does this happen because I am not returning the cached token correctly? I am implementing my code based on the standard example given for custom caching in the FB IOS SDK documentation.
It looks like your strategy is returning an FBAccessTokenData that has no permissions (which is not entirely unreasonable for your purposes unless you were to store permissions as well). When you init the session, however, specifying "basic_info" (while generally a good best practice, and required when asking for login that would prompt UI) will cause it to check that "basic_info" is included in the cached token. Since it is not, it will not load the cached token. For your purposes, you ought to be able to init the session with a nil permission array.
Related
I am using Laravel as my backend together with Sanctum which generates personal access token for mobile users. For my mobile application I am using flutter.
To authenticate users they login with their username/password and get a personal access token in return. This works but requires a user to login every time they open the application again so I did what most tutorials suggest which is saving the token on the mobile device using shared preferences/secure storage.
Now comes the question how do you invalidate a user when you remove their token from the backend? On initial login it appears everything is still fine because like in most tutorial I check for the existence of a token. After that whenever I want to make a request which uses the token I obviously run into problems because it not longer exists on the backend.
Most tutorials/guide suggest saving the token and using that a reference to see if the user is logged in or not but this seems flawed because it gives the false impression you actually have a valid token.
My guess is this can be solved by always performing a heartbeat/ping action to check if the current token is valid and if not send them to the login screen instead of simply checking for the existence of the token.
Thoughts on this?
I can suggest a hack or trick here in every launch of the app you can send a request to an API to check if the user's token is valid or not and if it is valid then you can continue the app otherwise force the user to login and generate new token this way your app will be secure via server / API.
For this, you can store the user's secret token in the database and check it via HTTP API call and send a response from the API accordingly and check the response in app and do the next operation according to the response you get.
I don't know if this is a great way of doing this job but it is a kind of hack/trick to achieve what is needed.
Thanks
I'm moderately familiar with OAuth2 and the concepts of the AccessToken and RefreshToken.
It looks like MSAL is doing some work for us when using ClientApplicationBase.AcquireTokenSilentAsync().
I'm not clear as to whether it will always check the expiration of the current AccessToken and automatically refresh it (using the RefreshToken) on method call.
Regardless, is there a "best practice" for how often we should call AcquireTokenSilentAsync() ? Should we keep track of the expiration ourselves and call this method to update our bearer authentication header? Should we be calling AcquireTokenSilentAsync() on every Request? (doubtful)
I can't see how the GraphServiceClient (tangent topic, I know) using the DelegateAuthenticationProvider will do anything helpful WRT refreshing. Do we need to extend that class and perform our own refresh when the token is nearing expiration? I feel like this would/should be already in the SDK.
Thanks for any tips.
-AJ
Update Nov 2020
This answer was originally written for V2 of the MSAL client. Since then a V3 has been released which may work differently from V2.
Original answer
I'm not clear as to whether it will always check the expiration of the current AccessToken and automatically refresh it (using the RefreshToken) on method call.
A refresh token is automatically supplied when the offline_access scope is provided, if I understand this answer correctly
...you've requested the offline_access scope so your app receives a Refresh Token.
The description of AcquireTokenSilentAsync implies that when an refresh token is provided, it will check the expiration date on the token, and get a new one if it's expired or close to expiring.
If access token is expired or close to expiration (within 5 minute
window), then refresh token (if available) is used to acquire a new
access token by making a network call.
It will repeat this behavior until the refresh token is expired. Optionally you can force a refresh of the access token via the refresh token by utilizing the forceRefresh parameter on AcquireTokenSilentAsync
Lastly, I am going to quote this answer on SO since it gives a nice insight about MSAL and tokens
Just to make a small clarification, MSAL doesn't actually issue tokens
or decide a token expiration, but rather ingests an acquires token
from the Azure AD STS.
MSAL will automatically refresh your access token after expiration
when calling AcquireTokenSilentAsync. .... The default token
expirations right now are:
Access Tokens: 1 hour
Refresh Tokens: 90 days, 14 day inactive sliding window
(June 13th '17)
Regardless, is there a "best practice" for how often we should call
AcquireTokenSilentAsync() ? Should we keep track of the expiration
ourselves and call this method to update our bearer authentication
header? Should we be calling AcquireTokenSilentAsync() on every
Request?
The documentation also lists a 'Recommended call pattern' for calling the AcquireTokenSilentAsync. The documentation also mentions that
For both Public client and confidential client applications, MSAL.NET maintains a token cache (or two caches in the case of confidential client applications), and applications should try to get a token from the cache first before any other means.
Based on examples I've seen, including the recommended call pattern from the documentation, I would argue you could simply call AcquireTokenSilentAsyncand catch the MsalUiRequiredException as an indication that the token has expired and the user has to log in again.
I can't see how the GraphServiceClient (tangent topic, I know) using the DelegateAuthenticationProvider will do anything helpful WRT refreshing. Do we need to extend that class and perform our own refresh when the token is nearing expiration? I feel like this would/should be already in the SDK.
If I understand the DelegateAuthenticationProvider correctly, what it does is modify the requestMessage before we pass it to Graph. All we got to do is provide our access token with an authorization header for the request. We already know that when we fetch our access token, it is valid, so we can just add it.
new DelegateAuthenticationProvider(async (requestMessage) =>
{
ConfidentialClientApplication cca = new ConfidentialClientApplication(_ClientId, _Authority, _RedirectUri, new ClientCredential(_ClientSecret), _UserTokenSessionCache.GetTokenCache(identifier, httpContext), _ApplicationTokenCache.GetTokenCache());
AuthenticationResult result = await cca.AcquireTokenSilentAsync();
requestMessage.Headers.Add("Authorization", result.CreateAuthorizationHeader());
//OR
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
});
(There is no difference between either way of setting the headers)
I've been down this path and this does the trick for me. I highly advise reading their documentation, because it does gives a good insight in how to implement MSAL.Net.
I haven't had time yet to play around with the token durations yet. Nor the behavior if no refresh token is provided (if that's even possible)
I hope this helps!
Mentioning one thing missed above, quoting my answer to Get refresh token with Azure AD V2.0 (MSAL) and Asp .Net Core 2.0
For context, OAuth 2.0 code grant flow mentions the following steps:
authorization, which returns auth_code
using auth_code, to fetch access_token (usually valid for 1 hr) and refresh_token
access_token is used to gain access to relevant resources
after access_token expires, refresh_token is used to get new access_token
MSAL.NET abstracts this concept of refresh_token via TokenCache.
There is an option to serialize TokenCache. See Token cache serialization in MSAL.NET. This is how to preserve sign-in info b/w desktop application sessions, and avoid those sign-in windows.
AcquireTokenSilentAsync is the process by which refresh_token is used to get new access_token, but, this is internally done. See AcquireTokenSilentAsync using a cached token for more details and other access patterns.
Hope this clarifies on why TokenCache is the 'new' refresh_token in MSAL.NET, and TokenCache is what you would need to serialize and save. There are libraries like Microsoft.Identity.Client.Extensions.Msal that aid in this.
#AlWeber/ #Raziel, the following pattern would apply for PublicClientApplication:
on startup, to deserialization and load TokenCache (which has refresh_token), try acquire access_token silently.
if that fails, use interactive UI to fetch token.
save and re-use the AuthenticationResult, which has AccessToken and ExpiresOn. Redo acquire access_token silently (bit expensive if you are an API user, hence caching of result), once you are close to ExpiresOn property (personally, I set 30 min before expiry).
is there a "best practice" for how often we should call AcquireTokenSilentAsync() ? Should we keep track of the expiration ourselves and call this method to update our bearer authentication header? Should we be calling AcquireTokenSilentAsync() on every Request? (doubtful)
I don't think this is a good idea. As mentioned, this call is still a bit expensive. Alternative, is to store AuthenticationResult in-memory, re-use it, and go to silent acquire workflow only close to ExpiresOn property.
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).
I've written an online converter and integrated Auth0 to my website. What I'm trying to achieve is to auto-upload the converted file to the Google Drive of a logged in user. I set up Google oauth in Auth0 and everything seemed to work fine.
The problem is, Google's access_token expires after 60min and I don't have a refresh_token. Therefore, the user needs to log in via the Google Login-page again. That is not, what I want, because the user is in fact logged in way longer than just 60min on my site, but Google refuses API-calls (because the Google token expired).
I know I can request a refresh_token by setting access_type=offline but this will add the permission Have offline access. I don't want that, I just want to upload data to the user's Drive, if he clicked the convert button on my page. I don't want to ask the users for permissions I don't need. If I (as a user) would link my Google account on a similar page and the tool asks for offline access I wouldn't approve, to be honest - the permission sounds like the tool creator can do whatever he wants with your account whenever he wants... There are many tools out there that have write access to a user's Drive without asking for offline access and with one single login until the user revokes the permission. How is that done?
Is there a way to make Google API calls without asking for offline access and without forcing the user to approve the app (that is already approved by him) again and again every 60min?
Thanks in advance,
phlo
Is there a way to make Google API calls without asking for offline access and without forcing the user to approve the app (that is already approved by him) again and again every 60min?
Yes there are ways, but it depends on the specifics of your use case. For example, is your code in Java/php/etc running on a server, or is it JavaScript running in the browser?
Running your auth in the browser is probably the simplest solution as the Google library (https://developers.google.com/api-client-library/javascript/features/authentication) does all the work for you.
By asking for offline access you are requesting a refresh token. Google is going to tell the user that you are requesting offline access. You can request something without telling the user what they are authorizing.
No there is no way to request a refresh token without displaying that message. Nor is there a way for you to change the message it's a standard Google thing.
I found the solution!
Prerequirements
Enable Use Auth0 instead of the IdP to do Single Sign On in your client's Dashboard
Create a new Angular-route to handle the silent login callback (e.g. /sso)
Add
$rootScope.$on("$locationChangeStart", function() {
if ($location.path().indexOf("sso") == -1) {
authService.relogin(); //this is your own service
}
});
to your run-function and set the callbackURL in angularAuth0Provider.init() to your new Angular-route (<YOUR_DOMAIN>/sso). Add this URL to your accepted callbacks in the Auth0 dashboard - this won't end in an infinite loop, because the locationChangeStart-event won't call authService.relogin() for this route
Add $window.close(); to the controller of the Angular-route (/sso) to auto-close the popup
Authenticate the user via Auth0 and save the timestamp and the Auth0-access_token somewhere
On reload:
Check, if the Auth0-token is still valid in authService.relogin(). If not, the user has to login again either way. If the token is valid and the Google token is about to expire (check this with the saved timestamp to prevent unnecessary API calls) check for SSO-data and login silently, if present
/* ... */
if (validToken && googleExpired) {
angularAuth0.getSSOData(function (err, data) {
var lastUsedConnection = data.lastUsedConnection;
var connectionName = (_.isUndefined(lastUsedConnection) ? undefined : lastUsedConnection.name);
var isGoogle = (_.isUndefined(connectionName) ? false : connectionName == "google-oauth2");
if (!err && data.sso && isGoogle) {
authManager.authenticate();
localStorage.setItem("last-relogin", new Date().getTime());
angularAuth0.signin({
popup: true,
connection: data.lastUsedConnection.name
});
}
});
}
Now you will find a fresh Google access_token for this user (without asking for offline access)
Resources:
https://auth0.com/docs/quickstart/spa/angularjs/03-session-handling
https://auth0.com/docs/quickstart/spa/angularjs/11-sso
Trying to organize this question into something clear. We are integrating Google for Work into our application, to use login, Google+, and eventually Contacts, Calendar, etc. As is recommended by Google and everything I have read, we are going to use incremental access, only adding scopes when they are needed. We are a PHP shop.
But, we will also be needing offline access, as our Contacts (and eventually Calendar) access will be synchronizing with our internal database.
We currently capture the Access and Refresh Tokens when doing the initial link, and store them locally, so that we can re-authorize at any time by using the Refresh token whenever the Access token expires. This is working correctly.
Questions:
a) when adding the incremental scopes for Contacts, the documentation says we need to call the gapi.auth.signIn() function in the page javascript with the new scopes. This is working on the page where we are allowing folks to manage settings. In the original login function callback, I save the Access Token and scopes with an Ajax call that uses the access code passed into the callback, and calls the Google_Client authenticate() function to get the access code and scopes... but at that point, the information I get back does not have the new scopes. Why? Do I have to re-extend the scopes every time the page is drawn?
b) since we are going to have a batch process do the contact synchronization, do I need to get an entirely different access token with access_type=offline, or can I use the current access token (properly extended with the new scopes). Can an off-line access token be used for on-line access as well as off-line? Or vice-versa?
For your questions:
a) have you used the parameter "include_granted_scopes"? as mentioned here:
https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth
b) When you request an offline access token, the response contains the access token and refresh token. so you can refresh the access token after it expires without having the user grant the permissions again.
online access token and offline access token work for the same.
the difference between both its the capability to refresh the access token when it expires without involving the user. Which is the functionality for the offline type.
The online access token doesn't mean that it works for your client-side authentication (done in the browser) and the offline works for the server-side.
You mentioned that you can get an access token, refresh token and authorization code from the client-side of your app. You could send that information to your server and make api calls from there, although this is not a good practice.
I would suggest that you do the OAuth Flow in the server side and from there manage the users information and API calls.
Here you can find the documentation on both Web server applications and Client Side applications.
Hope it's clearer.