I am using the library com.google.auth:google-auth-library-oauth2-http:0.9.0 in my Google App Engine application. My application makes requests to another application with its default application engine service account with domain delegation enabled. I have many users that I need to cache their tokens for one hour. The problem is that I do not know how to recover the access token before making the request.
credential = GoogleCredentials.getApplicationDefault()
.createScoped(scopes)
.createDelegated(user).toBuilder().build();
At this moment access token is null.
credential.getAccessToken()
And if I update the token to retrieve a new access token, an error occurs:
credential.refreshAccessToken();
The OAuth2Credentials instance does not support updating the access token. You must use an instance with a new access token or a derived type that is compatible with the update.
The request is made with the google-api-client library.
import com.google.api.client.http.*;
import com.google.api.client.extensions.appengine.http.UrlFetchTransport;
import com.google.auth.http.HttpCredentialsAdapter;
HttpRequestFactory factory = new UrlFetchTransport().createRequestFactory(new
HttpCredentialsAdapter(credential));
Thanks.
Related
I am working on a SDK where we send telemetry(1000s of events/minute) to Ingestion Service owned by Microsoft.(SDK is used by webapps hosted on Azure VMS or App services) The Ingestion Service currently support authentication using Managed Identities (both system and user assigned). My idea is to take a dependency on the Azure Identity SDK and use the existing DefaultAzureCredential or ManagedIdentityCredential implementations of 'TokenCredential' to get the tokens as shown below and Use this defaultCredential while initializing the sdk. Once I have the sdk initialized my idea is to get the token and attach to authorization header on each request.
DefaultAzureCredential defaultCredential = new DefaultAzureCredentialBuilder()
.managedIdentityClientId("<MANAGED_IDENTITY_CLIENT_ID>")
.build();
Questions:
How frequently are the tokens refreshed? How to control the expiry of these tokens?
When the token is expired, are the managed Identities smart enough to call the AAD and get new tokens. Or is it the SDKs(client) responsibility to get a new token and update the header.
In my opinion, the token won't be refreshed automatically, and if you wanna know the expired time, you could use tools like fiddler to catch the request which used the token and use jwo.io to decode it, you will get a claim of 'exp', it's a unix timestamp, you can convert it then you can know the expire time.
And the expire time is managed by the policy of your tenant, you can follow the tutorial to create a policy for your access token. And because your code just new DefaultAzureCredential(), any time execute the line, it will generate a new instance so that you don't worry about the token expired.
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.
I have 2 web apis (A and B) on my b2c. Each one of them publishes their own permissions respectively (scopeA1, scopeA2) and (scopeB1, scopeB2).
On my web application (which already configured and have granted access permission on both apis and the 4 scopes), in order to get authorization code for both apis during authentication, I tried to set my OpenIdConnectAuthenticationOptionsin scope property to include the 4 scopes.
I got an error AADB2C90146: The scope 'scopeA1 scopeA2 scopeB1 scopeB2 openid offline_access' provided in request specifies more than one resource for an access token, which is not supported.
While if I specify only scopes for web api A or B, then it works as per this link
How can I get my web app to use both web apis even with granted permissions for both
Thanks for help
If the two web APIs are separate applications in Azure AD, then you need to request access tokens separately for them.
I'm not familiar with the sample you used as a starting point, but it looks like these lines are where you need to make your change:
// Retrieve the token using the provided scopes
ConfidentialClientApplication app = new ConfidentialClientApplication(authority, Startup.ClientId,
Startup.RedirectUri, credential,
new NaiveSessionCache(userObjectID, this.HttpContext));
AuthenticationResult result = await app.AcquireTokenSilentAsync(scope);
accessToken = result.Token;
You should create an app instance for each of your APIs, and acquire a token for each of them. Then, when you call the APIs somewhere else, use the correct access token in the Bearer authentication header.
I had the same issue and asked a similar question Extend MSAL to support multiple Web APIs
but i have not had an answer, basically to get around it in the short term i have made both my API's use the same authorization client ID + secret and therefore I can reuse the same scopes accross my APIS
its not what i want but if you want to use Azure AD B2C you need to get used to compromising for a while until the support is there
-- I would also say you are using an older version of MSAL which i am also using, im waiting until the version 1 release before upgrading again.
The github talks about using this format
https://github.com/AzureAD/microsoft-authentication-library-for-dotnet
Step 1: Add MSAL to your Solution/Project
Right click on your project > Manage packages.
Select include prerelease > search msal.
Select the Microsoft.Identity.Client package > install.
Step 2: Instantiate MSAL and Acquire a Token
Create a new PublicClientApplication instance. Make sure to fill in your
app/client id
PublicClientApplication myApp = new PublicClientApplication(CLIENT_ID);
Acquire a token
AuthenticationResult authenticationResult = await
myApp.AcquireTokenAsync(SCOPES).ConfigureAwait(false);
Step 3: Use the token!
The access token can now be used in an HTTP Bearer request.
So we have an application that allows you to create a xml file that runs the app again at a later stage (which may or may not have a user in attendance). Files are stored on the user cloud drive platform of choice. So the process is
Workflow 1
Authenticate to cloud with User 1 details/input
Select files to Download and use
Save and encrypt file metadata and Refresh token to xml file. (app workflow 1)
Workflow 2 ( can be repeated multiple thousands of times)
Send xml file to another pc with User 2. (either by email or remotely through a console
that pc then runs under user 2
start app
App authenticates automatically using refresh token saved in xml file with no user input (as there is a very high chance of the user who created workflow 1 not being in the same city as where workflow 2 is running
Downloads files
applies files (app workflow 2)
PROBLEM
all other platforms we cater for (Dropbox and google and onedrive) gives us access to the refresh token and allow us to authenticate with it again , however the onedrive for business (graph sdks) give us a Token cache which is session based?
Questions
So I need to know how I can get the refresh token from the Token Cache so we can reuse it at a later stage. (yes I'm aware that it will expire after 6 months which is acceptable) .
When i have the refresh token how do initiate a call to refresh the token
Further note - I have managed to handle all platforms before Within silverlight (where the sdks are not supported) through directly calling the rest api calls but we are converting our solution to WPF and would want to use the sdks
thanks
I assume you were developing Microsoft Graph SDK when you mentioned graph sdks.
AFAIK, this library does not include any default authentication implementations. Instead, the user will want to authenticate with the library of their choice, or against the OAuth endpoint directly, and built-in DelegateAuthenticationProvider class to authenticate each request.
And if you were authenticating Azure AD with Active Directory Authentication Library, you can use the default token cache which use memory to store the cache or you can implement the token cache based on your requirement.
So I need to know how I can get the refresh token from the Token Cache so we can reuse it at a later stage. (yes I'm aware that it will expire after 6 months which is acceptable) .
When you using the ADAL library, there is no need to get the refresh token manually, it will handle for us to renew the access token if the refresh token is existed.
When i have the refresh token how do initiate a call to refresh the token
If you want to perform the request yourself to refresh the access token, you can refer the request below, and more detail you can refer this document.
POST /{tenant}/oauth2/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&resource=https%3A%2F%2Fservice.contoso.com%2F
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps
OK so the answer is taking the tokencache from the PublicIdentityApp and serializing as below.
var tokenCache = _app.UserTokenCache;
var tokenBytes = tokenCache.Serialize();
var tokenString = Convert.ToBase64String(tokenBytes);
And deserializing it later.
_app = new Microsoft.Identity.Client.PublicClientApplication(ClientID);
var array = Convert.FromBase64String(bytestring);
_app.UserTokenCache.Deserialize(array);
authHelper = new AuthenticationHelper(_app);
var authorise = await authHelper.GetTokenForUserAsync()
I have developed a web app that uses Azure Active Directory to authenticate. The application is redirected to AAD, which after authentication, stores a token in a token cache provided by the application. The app then uses Entity framework to retrieve the token, using the location provided by default location, and by default this points to the local database.
My question - is there any way I can change the location of the token store to either a table service or the application cache provided by Azure. I think this will involve configuration on the Active Directory as well as changing the method that retrieves the token.
Are there any open source libraries that will let me do this? Also, are there any publicly available samples that demonstrate this process?
You can use the custom token cache provided by MSAL.In custom token cache, retrive the token and then make an api call to store it anywhere you want.
Here is the api detail
https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/token-cache-serialization
Here is a sample use of the custom token cache
https://github.com/agrabhi/active-directory-b2c-graph-trustframework-policy/blob/master/console-csharp-trustFrameworkPolicy/TokenCacheHelper.cs
Curious, why would you want to store customer tokens though.