How can i get a token expiry when i authenticate to Salesforce using OAuth2? - salesforce

I have set up a connected app with the following OAuth scopes
Access the identity URL service (id, profile, email, address, phone)
Manage user data via APIs (api)
Manage user data via Web browsers (web)
Perform requests at any time (refresh_token, offline_access)
Access custom permissions (custom_permissions)
I first authenticate using the following
$"{this.ServiceUrl}/authorize?response_type=code&client_id={this.ClientId}&redirect_uri=<HOST_NAME>/SalesForce/MySFCallback";
this presents me with the Salesforce login screen and once i have successfully logged in , I am returned back to my web page and then i try to
get a token as follows:
Using the code value returned from the callback , I call
var client = new RestClient(Uri + "/token");
var request = new RestRequest("", Method.POST);
request.AddParameter("grant_type", "authorization_code", ParameterType.GetOrPost);
request.AddParameter("code", code, ParameterType.GetOrPost);
request.AddParameter("client_id", clientId, ParameterType.GetOrPost);
request.AddParameter("client_secret", clientsecret, ParameterType.GetOrPost);
request.AddParameter("redirect_uri", $"{callbackUri}", ParameterType.GetOrPost);
var response = client.Execute(request);
I get back the JSON response , but there is no expiry for the token.
How can i get an expiry for the token ?

Have you seen https://salesforce.stackexchange.com/q/73512/799 ? Very good answers there.
In a pinch your administrator could try creating a custom number field on User based on user's profile and then expose it to you as a custom attribute (you'd see the extra field in the base64-encoded id_token piece, if you requested this response_type).
But really admin can:
terminate sessions at any time (Setup -> Session Management),
revoke your connected app's access
other things can kick in like you switching IPs (from office vpn to home network or cellular data plan) and company having Setup -> Session Settings -> Lock sessions to IP from which they originated...
So at best this thing would be indicative timeout.
Try to code it defensively based on refresh tokens like in sfdcfox's answer

Related

How to read users gmail after receiving push notification from gmail users.watch API?

I want to implement a backend server that can read (to perform some action) users gmail every time a new mail is received. I am able to figure out that using gmail API users.watch, my server can be notified every time a new email is received. Now, for fetching new mails from gmail my server needs User credentials (Auth token) that are provided by the user at the time of opting in to be watched. Is there anyway these credentials can be sent to my server along with the push notification (maybe using users.watch API).
One method I came across to achieve the same is to store auth and refresh token in a DB, that will be accessible only by my server. But it will be better if the purpose can be achieved without storing credentials in the DB.
When the user authenticates your application you are given a refresh token assuming that you requested offline access. You should store this in a secure place associated with the user on your server.
When you get a push notification you should then retrieve the refresh token that you have stored on your server and use that to request a new access token that you can use to access the users data.
The push notification system has no way of sending you the authorization nor would it be a very wise idea if it was storing your authorization.

How does AAD API Access delegate permission work?

I'm having a little trouble following how API Access delegate permissions work with azure active directory. I feel like i'm probably misunderstanding a key aspect of how AAD works.
Here is my set up
I have a Web Application let’s call it WebApp. I have created
an AAD for the Web Application and registered with a AAD App ID. Let’s
call it App ID A
I have a Web Api let’s call it ApiService. I have also created an AAD for it and registered with a AAD App ID. Let’s all it App ID B.
In AAD App ID A, I have updated the clicked on the API Access ->
Required Permissions -> Add (App ID B ; Web API) permissions
I’ve updated the manaifest in the AAD App ID B, to give consent to
knownClientApplications to include the client ID of the Web App
I’ve also enable oauth2AllowImplicitFlow to be true for both App’s
manifest.
What I’m trying to do is, A user signs into the web application sign. When it signs in, the user is able to acquire a token for the specific Web App App ID A. The user should be able to use that token and have access the Api Service with App ID B. I thought by configuring the whole API Access -> Required Permissions within the Web Application it would give me delegate permission with the logged in user to communicate with the Api Service WebApi.
When I examine the JWT token, I notice that there is a claim for Microsoft Graph, but not for the ApiService. Shouldn’t I be seeing a claim?
When I try to use the token, it reacts with a 404 authentication error.
Any advice appreciated,
Thanks,
Derek
UPDATE
In response to #joonasw
I actually looked at the example you wrote when i started.
https://joonasw.net/view/aspnet-core-2-azure-ad-authentication
In the example, the web application is initialized with:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("OpenIdConnect").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = ctx =>
{
return Task.CompletedTask;
}
};
});
In the HomeController, there is code to retrieve the token for the graph api
private async Task<string> GetAccessTokenAsync()
{
string authority = _authOptions.Authority;
string userId = User.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
var cache = new AdalDistributedTokenCache(_cache, _dataProtectionProvider, userId);
var authContext = new AuthenticationContext(authority, cache);
//App's credentials may be needed if access tokens need to be refreshed with a refresh token
string clientId = _authOptions.ClientId;
string clientSecret = _authOptions.ClientSecret;
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync(
"https://graph.microsoft.com",
credential,
new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
From my understanding, when the user initially login to the web application it will trigger the OnAuthorizationCodeReceived() method where it will be using the clientId/clientSecret/resource of the web applicaiton. The token is stored in the distributed token cache under the key resource/client id.
In the example, GetAccessTokenAsync() is used to grab the token to access the graph API.
In my case, I was hoping to update that method to retrieve the token for the WebApi which has a different clientId/clientSecret/resoruce. In my case, it will AcquireTokenSilentAsync will throw an AdalTokenAcquisitionExceptionFilter because the token needed is not stored in the cache and in the AdalTokenAcquisitionExceptionFilter it will call try to reauthenticate
context.Result = new ChallengeResult();
which will redirect to the authentication page and then hits the AddOpenIdConnect() method. However, the openIdConnect is configured with the web app clientID/ClientSecret/Resource and will not store the new token properly. It will try to call GetAccessTokenAsync() again and the whole process will go in an infinite loop.
In the example, if you were to comment out the "Anthentication:resource" in app.settings, you will experience the same issue with the infinite loop. What happens is that you initially authenticate correctly with no resource specified. Then when you click on you try to get the token for microsoft graph which is a new resource, it can't find it in the cache and then tries to reauthenticate over and over again.
I also notice that the acquireAsyncAuthentication only returns a AuthenticationResult with a bearer tokentype. How would you get the refresh token in this case?
Any advice?
Thanks,
Derek
UPDATE (Solution)
Thanks to #jaanus. All you have to do is update the resource to the clientid of the web api and pass that into AcquireTokenSilentAsync. The web api id uri that you can get from the azure portal did not work.
Okay, so it seems there are multiple questions here. I'll try to make some sense of this stuff to you.
Adding the "Web App"'s client id to the "ApiService" knownClientApplications is a good idea.
It allows for consent to be done for both apps at the same time. This really only matters for multi-tenant scenarios though.
Now, your Web App will be acquiring access tokens at some point.
When it does, it must specify a resource parameter.
This parameter says to AAD which API you wish to call.
In the case of the "ApiService", you should use either its client id or Application ID URI (this is more common).
Depending on the type of your Web App, the access token is acquired a bit differently.
For "traditional" back-end apps, the Authorization Code Grant flow is usually used.
In this flow your back-end gets an authorization code after the user logs in, and your Web App can then exchange that code for the access token.
In the case of a front-end JavaScript app, you would use the Implicit Grant flow, which you have allowed (no need to enable it in the API by the way).
This one allows you to get access tokens directly from the authorization endpoint (/oauth2/authorize) without talking to the token endpoint as you usually have to.
You can actually get the access token right away after login in the fragment of the URL if you wish.
ADAL.JS makes this quite a lot easier for you if you are going in this route.
The reason you get the authentication error is because the access token is probably meant for Microsoft Graph API. You need to request an access token for your API.
An access token is always only valid for one API.

Access AngularJs application via link from email

I need some suggestions from people who maybe had an experience with similar types of applications.
I am building and app that should be use for on line testing. For example if you want a job in some company, HR can create test session for you, and send you a link to an app.
That email will contain a link.
On link click I want to hit my angularjs app. AngularJs will get session id from the link it should make a request to server.
Server then needs to check db and try to find a test session with that Id, and return some response. (session data if it's ok or some message that session is expired, etc.)
The problem here is usage of JWT tokens. For example, when HR uses part of app, it has to be authenticated, which is done by jwt tokens.
What is the best practice when you have users on the other side? Like people who will take the test. They do not have jwt token, but server will expect something for authentication.
Should user get some custom token that will expired when test session expires, then maybe I can decode that token on angular side and check user role or something like that.
Did anybody had experience with building an app that can be access over a link?
Here is a possible scenario
HR connect to the app and generate a new test session
Create a new test session in the database with all the necessary info + add 2 columns : Token + Token_Expiration. The Token will be a random string and the Token_Expiration is now + x hours
Send link with token to the user by email. e.g : http://myapp.com/?token=ahdk5d5ek4945gol
User click on the link that redirects him to the landing page.
Retrieve the token from the url and send it to the server
Server must check if this token exists inside the database
If the token does not exist - Forbidden access
If the token exists - Check if the current time does not exceed the Token_Expiration
User finish the test
Server must remove the Tokenor use a third column Valid and set it to false
Send custom token with link and based on that authenticate user
then find session id for that custom token and return response
and invalidate custom token after completion of test.

Google OAuth2 flow and id_token refresh

I am having troubles in implementing OAuth in the right way.
I use a client/API architecture (Angular for front and Node.js for back) and I would like user to sign in using Google OAuth authentication only.
Here is what I think is the right way for the moment (tell me if I misunderstood something) :
Angular open a Google popup asking user's consent.
Once the user agree, Google Authorization server sends back to angular a verification code.
This verification code is forwarded to an API endpoint.
Then, the API asks Google Authorization server to exchange this code for an access_token, an id_token and a refresh_token.
Google sends those 3 tokens.
The API uses access_token to retrieve user from Google API
The API persists the user
Here is the little dillema, in my opinion, the access_token and refresh_token should be stored into the database and the id_token should be sent back to Angular client.
This way, it would allow the API to ask for resource in Google API and if the token expires it can still ask for a new token thanks to the refresh_token.
Client-side, the id_token is embedded in all requests thus allowing the API to identify the client and verify his authentication with Google certs from https://www.googleapis.com/oauth2/v3/certs.
Supposing this is right way to use tokens, how could I deal with id_token expiration since client does not have any refresh token ?
Thanks !
I do it slightly different (I have the same basic architecture though).
Angular decides the user needs to log in and displays a login popup.
The url in the login popup is not serviced by angular, but is instead directly run off of the backend server: /auth/google . (I use hapijs and bell, personally).
/auth/google is serviced by a bell plugin and initiates the OAUTH dance.
the end of the OAUTH dance results in my node server generating a local token (I just generate random bytes and store them in redis mapped to user ids)
because the initial login popup was created by window.open, the success page (generated on the api side rather than in angular) can use window.opener.postMessage to communicate the token back to the angular runtime.
This way, all my sensitive google credentials (the user's oauth token, refresh token if needed, and my application's api ID and secret) are only on the server, except for during the OAUTH dance relay when they're in a URL string during the client redirects. This is reasonably secure.
Then for all the actual user interactions with the api, I use the token I generated in step four to authenticate. This could be a JWT if you wanted, but I don't do it that way; I just use redis to map from 'longrandostring' -> userId. That lets me (for example) force everyone to re-login if I wipe the redis database that has all the tokens stored, or I can write a lua script to delete all the entries that map to a certain userid.
If you need a refresh token, you can set access_type=offline in the initial request to oauth2/auth, and you'll get a refresh token as part of the response, unless you've previously gotten a refresh token. You can then persist it on the server side and get new access tokens as needed. If you set approval_prompt=force as well, you'll force a new consent screen and be guaranteed a refresh token (but after some small number of refresh tokens granted to a user, older ones expire on the same application so it's best to only request them if really needed).

Retrieve a logged in Google Glass User's email address?

We are attempting to be able to provide the ability for a Glass user to request an email to be sent to them from a Timeline Card. On the notify callback Servlet, we are attempting the following to retrieve a user's email address:
String userId = notification.getUserToken();
Credential credential = AuthUtil.getCredential(userId);
Mirror mirrorClient = MirrorClient.getMirror(credential);
Contact contact = MirrorClient.getContact(credential, userId);
We do not get a result back when retrieving an email off of the UserInfo object of a authenticated user. Our application has the following scopes available to the application server:
"https://www.googleapis.com/auth/glass.timeline "
"https://www.googleapis.com/auth/glass.location "
"https://www.googleapis.com/auth/userinfo.profile "
"https://www.googleapis.com/auth/userinfo.email "
"https://www.googleapis.com/auth/contacts"
Are we allowed to retrieve the authenticated user's email address, is there a permission I am missing or is there another means by which to request that data?
The getContact call you are making doesn't have anything to do with the user's email address. You can read up on what Contact is referring to here:
https://developers.google.com/glass/contacts
To get the user's email address, I've successfully used the same auth token used to authorize the Glass mirror API app with the added scope you mention to call this URL:
https://www.googleapis.com/userinfo/email?alt=json
This method seems to stop working after the initial authorization at some point, so be sure to do it when the user first authorizes the app and save off the email.
Although I've also just gotten the email off AppEngine's UserService before as well, which is easier if you happen to be running on AppEngine:
https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/users/UserService
So the question boils down to "Why am I not getting contact info for this userID that I am sending to the Google Mirror service?"
The Mirror service only provides contact information for Contacts that your Glassware has added. See https://developers.google.com/glass/contacts for more about Contacts in Glass and how to add Contacts. Unless you have already added a Mirror Contact with this userId, you won't get anything back.
The Mirror service does not provide direct access to the information from userinfo.info or userinfo.email. You will need to get it out using the OAuth2 libraries first if you want to add them as a Contact for Glass.

Resources