Firebase Admin SDK handle expired access token - c#

I'm using the Firebase Admin SDK with ASP.net Core and because we are using workload identity federation instead of the service account key json file i'm creating my FirebaseApp instance this way:
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromAccessToken(serviceAccountToken),
ProjectId = projectNo
})
Where "serviceAccountToken" is a Google Access Token received in a previous step.
This works like intended, but the access token i'm using expires after 1 hour.
My problem: I couldn't find a way to update the existing FirebaseApp instance with a new access token. My best approach is to use FirebaseApp.DefaultInstance.Delete() to dispose the current instance and then create a new one, but that seems a bit heavy for just updating the access token.
Is there a better approach?
Update:
My next attempt was simply to assign a new token after the token expired via FirebaseApp.DefaultInstance.Options.Credentials = GoogleCredential.FromAccessToken (newToken), but the new credentials are not taken into account.
After reading through the 'google-api-dotnet-client' source code, I noticed that GoogleCredential.FromAccessToken uses AccessTokenCredentials internally (see github). So I tried to write my own implementation of AccessTokenCredential and implement a refresh function there, unfortunately the interfaces for it are all declared internal.
In the end, there is still no good solution.

Related

How can I get a new OAuth access token from an external provider

I am trying to renew my external providers OAuth access tokens after they expire. They are stored in my SQL database via the signin manager once the user has logged in using the external provider originally.
The access token is used by the server to make requests on the users behalf (mostly to update my database with some external providers information every now-and-then).
I am using a .NETCore 3 preview-6 Angular starter project with built-in Identity Server (I realize this a preview - but I think my issue is more related to a lack of understanding what I should be doing!). I haven't had too much trouble getting the original external login working (using .AddAuthentication().AddOAuth(...)).
But when my service that is using the provided access token gets a 401 response, I cannot figure how to get my server to renew the access token for the user silently.
I have tried using [Authorize(AuthenticationSchemes = "MyScheme")] on my Controller but this has the external provider responding with a CORS error. I then tried HttpContext.ChallengeAsync(...) in my service, but that just didn't even seem to work at all (maybe this is related to the preview version of .NETCore?).
I have even tried to re-create the scaffolded code that returns a new ChallengeResult(...), but I get the same CORS error when called from my API. (I am unsure what black-magic is going on here to make the request appear like the referrer is the external provider.)
TL;DR (code snippet)
Ideally, I am trying to make the // TODO section work:
// This code is inside a Service I have which is called by a Controller action
var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);
var accessToken = await _userManager.GetAuthenticationTokenAsync(user, "MyScheme", "access_token");
_client.SetBearerToken(accessToken);
var response = await _client.GetAsync($"{uri}?{_query.ToString()}");
if (!response.IsSuccessStatusCode) // Just assuming 401 for simplicity
{
// TODO: renew the access token and try again
}
I am expecting to get an updated access token back that I can use to re-do the request. Ideally this would be a silent renew, or a redirect if the requested external access has changed.
Hope this makes sense, please let me know if you need more information.
Thanks

Obtaining bearer access token to access o365 resource, using Azure AD auth

I'm using the bog-standard, file->new Asp.Net Core Web Application (Razor Pages) project, and configuring it to use Azure AD authentication against an o365 instance, which works just fine.
I now want to use the app to access an o365 resource (e.g. my calendar) using the Graph API. In asp.net core 2.0, I used the method described here to obtain the access token, cache it, and retrieve it for any graph requests. It relies on an OpenIdConnect event (OnAuthorizationCodeReceived) to obtain the access code.
I don't see any similar event on the new AddAzureAd method available using asp.net core 2.1. Is there now a new method for obtaining the token for use in Graph calls?
This always has been a complex question and depending on your scenario (permissions required, workloads you're talking too) this answer might need to be adjusted.
First thing, here, you have the code grant and the id_token. The easiest way to achieve way to achieve what you want to achieve (may not be the best in terms of user experience) it to store the id token temporary. (let's say in a token cache)
services.AddAuthentication()
.AddOpenIdConnect(opts =>
{
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = ctx =>
{
return Task.CompletedTask;
}
};
});
You can see an example here you also need to make sure your application is configured with the proper permissions and you should be ready to go!

Get comments thread for a YouTube video using api v3.0

I'm trying to use the .net YouTube API v.3.0 to retrieve the comments thread. I'm basically trying to reuse the sample for Java, but when I try to authorize with my client secrets I do not get any scope with my token (it is set to null when I inspect the credential variable). Here is the code I literally took from .net example:
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeForceSsl},
"user",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
I can list all the videos I uploaded, but I cannot get the comments thread for them using the code below:
var req = youtubeService.CommentThreads.List("snippet");
req.VideoId = playlistItem.Snippet.ResourceId.VideoId;
req.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText;
var res = req.Execute();
The "youtubeService" has been created by passing the "credential" variable created earlier. Anyone had more luck with getting comments using .net?
Repro steps: I had to sleep with this to solve it - it turns out that I was making changes to the app multiple times, and initially I only requested to read the playlists (as by this example: Retrieve my uploads). Therefore I authorized with a scope:
YouTubeService.Scope.YoutubeReadonly
This scope obviously is not enough to read CommentThreads, but when I changed the scope the app kept the same authorization token and used it with all my requests (with the scope set to read-only).
What I did to solve this problem: I revoked the token from the code of my app and authorized again. This time, because there was no token cached, a consent window appeared with a scope to "manage your account" (before it was "read-only access"), and I was able to obtain a token with a proper scope this time.
Code to revoke token:
await credential.RevokeTokenAsync(CancellationToken.None);
Expectations: I was expecting that because I changed the scope in my code, the consent window will appear again without the need to revoke the token, because my cached token had a lower/different scope.
Summary: The issue (bug or by design?) is that once you obtain token within your app and want to change the scope (to the more powerful one), you actually need to revoke the token and authorize again, otherwise you will end up getting the same token with initial scope, even though it has been changed in the code.

Google Contacts API with Service Account issue

So, I've basically got this working, except for one issue. I've got a google service account set up so it can access our domain contacts. And it can batch query them perfectly!
But if I call cr.Retrieve("some-contact-url-here"), it throws an error griping about not having a Refresh token. I'm using a service account though, so I don't get a refresh token when I authenticate.
And I can't seem to find any good answer as to how I'm supposed to get a refresh token for a service account. There's one or two stackoverflow posts which actively mention getting a refresh token with a service account....but what they linked to has since been redirected. Anything else I've found to do with refresh tokens has basically been about authenticating manually and storing the token. Because I need to use a Service Account, that is not a possibility.
A service account's credentials, which you obtain from the Google Developers Console, include a generated email address that is unique, a client ID, and at least one public/private key pair. You use the client ID and one private key to create a signed JWT and construct an access-token request in the appropriate format. Your application then sends the token request to the Google OAuth 2.0 Authorization Server, which returns an access token. The application uses the token to access a Google API. When the token expires, the application repeats the process.
Check this page for more information.
Ok, based on several hours of bashing my head violently against the API, it looks like there's basically no way to get a refresh token when you're authenticating as a Service Account. Expected behavior, really.
Anyway, to get around the issue, you can load all of the contacts into memory,
feed.AutoPaging = true;
foreach (var c in feed.Entries)
{
contactList.Add(c);
}
And you can update|delete|etc then. Grossly inefficient though. Especially if your contact list gets rather big.

Persisting the OAuth2 bearer token when using Thinktecture Identity Server

I've been following the Thinktecture Identity Server example of OAuth2 Resource Owner Password Flow found at http://leastprivilege.com/2012/11/01/oauth2-in-thinktecture-identityserver-v2-resource-owner-password-flow/
I have the example working and returning JWT tokens successfully via the following process
Use the Thinktecture OAuth2Client to retrieve the access token
Retrieve the signing certificate from the "Trusted People" store on the client machine
Using the certificate and creating a new JwtSecurityTokenHandler and TokenValidationParameters and calling tokenHandler.ValidateToken to get a ClaimsPrincipal
From here I am authorized, but I am uncertain of the best way to persist the token for further requests. I tried using
var sessionToken = new SessionSecurityToken(principal, TimeSpan.FromHour(8));
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
But I do not have a SessionAuthenticationModule registered. I tried using the Identity and Access wizard to get this in place, but it makes many changes to config and tries to set things up for passive authentication.
I could use a traditional FormsAuthentication cookie (.aspnetAuth) but I remember discussion that an advantage of the .FedAuth cookie was that it was naturally split into several cookies if the size grew too big.
I'm struggling to find an article that completes the picture for me. I need the bearer token for accessing various APIs further down the stack. I have working examples of this for SSO/passive authentication, because most of the work is done for you. I'm just not sure of the best pattern for use when using the Resource Owner Password flow.
So
Have I missed a more straightforward way to achieve this with Thinktecture Identity Model and Server?
Should I try to create a FedAuth cookie so that I can reuse the various Messagehandler/filter components that are already setup for WIF?
Otherwise - is there anything particularly wrong with simply putting the access token in the UserData section of the FormsAuthentication cookie?
Try to look at this question: WIF Security Token Caching.
I believe this code might do
var sessionSecurityToken = new SessionSecurityToken(principal, TimeSpan.FromHours(Convert.ToInt32(System.Web.Configuration.WebConfigurationManager.AppSettings["SessionSecurityTokenLifeTime"])))
{
IsPersistent = true, // Make persistent
IsReferenceMode = true // Cache on server
};
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);

Categories

Resources