Managing Tokens in .NET MAUI

Learn the best practices for securing ID, access, and refresh tokens in your .NET MAUI applications and keeping a consistent user experience.
Read more…

:writing_hand:t2: Brought to you by @andrea.chiarelli

How did you like this post? Please share any comments or feedback with us on this thread

I’ve noticed that once your Id token has expired that the oidc middleware throws an uncaught validationException when checking the expiry on the jwt.

I’ve caught this in the GetAuthenticatedUser method and called logoutasync. Is this an acceptable workaround, and is there a better way of managing this?

Hi @Aranict,
Welcome to the Auth0 Community! :wave:

This is an acceptable approach if you want to close your user session when the ID token expires.
If you want to keep the session active (i.e., close it only when the user explicitly logs out), you can call the RefreshTokenAsync() method to refresh all the tokens.

The article focuses on refreshing access tokens, but it also applies to ID tokens

2 Likes

@andrea.chiarelli

Hello,

I have followed this code precisely. Maui app, targeting Android, iOS and Windows. All .Net7, latest OS on each.

This sets the callback:

        builder.Services.AddSingleton(new Auth0Client(new()
        {
            Domain = "mycompany.auth0.com",
            ClientId = "xxxxxxxxxxxxxxxxx",
            Scope = "openid profile email",
#if WINDOWS
            RedirectUri = "http://localhost/callback"
#else
      RedirectUri = "com.mycompany.mycompany://callback"
#endif
        }));

I keep getting this error:

One or more errors occurred. (You need to declare the windows.protocol usage of the protocol/scheme "http" in your AppxManifest.xml file)'

[External Code]
    mycompany.Auth0.WebBrowserAuthenticator.InvokeAsync(IdentityModel.OidcClient.Browser.BrowserOptions, System.Threading.CancellationToken) in WebBrowserAuthenticator.cs
    [External Code]
    mycompany.Auth0.Auth0Client.LoginAsync() in Auth0Client.cs
    mycompany.MainPage..ctor.AnonymousMethod__0() in MainPage.xaml.cs

Cannot find much on editing the AppxManifest.xml file and no access to it in VS either unless I’m missing something.
Wonder if this might be a local Windows issue as I try run this on my local/dev machine.

EDIT: And in Android I get this:

One or more errors occurred. (You must subclass the WebAuthenticatorCallbackActivity and create an IntentFilter for it which matches your callbackUrl.)'

This sample I think needs to be expanded to include some of this info as there are obviously some new/additional steps missing. It is hard to keep up with the pace of change and moving technical targets, I do realize that!

Thanks

This is a duplicate question. I replied here

1 Like

Hey Andrea,

very nice solution, thank you very much! Unfortunately, I’m only encountering one problem here. If line

var responseMessage = await base.SendAsync(request, cancellationToken);

is reached and it is determined that 401 is Unauthorized, then I get the message after updating the token:

System.ObjectDisposedException: Cannot access a closed Stream.

Do you got any ideas?

Hi @AllDayCare, are you getting this error by running the exact code of the article or are you running your custom code?
In the second case, are you surrounding your code with any using statement?

Hi @andrea.chiarelli,

yes, i used the exact code of the article.
No, im not using any “using” statements.

For me it also make sens, because if i get a 401 as return the communication between client and server is over for this request.

I am now trying another option. Since I always query whether the user is currently authenticated when starting the app or switching the page (this is frequently), I also include the Access_Token_Expires_In and check whether it has expired or expires in less then five minutes. If so, I use the code of the article to update with the refresh token.
Can you briefly confirm that this process makes sense and has no security vulnerability?

For me it also make sens, because if i get a 401 as return the communication between client and server is over for this request.

Not sure :thinking: The request failing because of 401 and the new request are separate HTTP requests.

I am now trying another option. Since I always query whether the user is currently authenticated when starting the app or switching the page (this is frequently), I also include the Access_Token_Expires_In and check whether it has expired or expires in less then five minutes. If so, I use the code of the article to update with the refresh token.
Can you briefly confirm that this process makes sense and has no security vulnerability?

Inspecting the access token on the client side is not a good practice. More than for security reasons, it’s for compliance reasons. See here.

Hello, i used another blog post that uses dot net and maui v8
in that blog the “auth0 client” comes from a github / nuget package.
i think that this blog post should be updated to use V8 and use the auth0 client code from the other blog post.
for me when i tried to use the code here matched to my v8 code it breaks and fails.

i think that i can use the other v8 code and just add the token secure storage model and then i might have a working app again.

working on that right now and if it works i will try to add some feedback on how that works for me.

the other blog post is this one:

Hey @figuerres,
This article along with this other article about MAUI are in the process of being updated.
We recently released the MAUI SDK and I’m going to update this MAUI content in the next few weeks.
Sorry for the inconvenience :pray:

no problem i know that Microsoft keeps changing and updating …

over the weekend i came up with some code that i working for me right now.

this may not be the best way but what i have seems to be working good so far.

i took the new auth0 client and made a class that i am calling LoginState
this class is a singleton that has the auth0 client as a private member.

it exposes the login/ logout refresh and get authenticated user methods.
it also handles my use of secure storage of the tokens and has properties to give the app the user name and other attributes my app needs.

there are a few things i have not been able to do yet but nothing that is keeping the project from moving forward at this point.

one item to note is that the browser windows that are created for login are not automatically closing. i did file a bug report on that.
not a show stopper but if that can be fixed it will make the app look better to users.
i have not tested but when i have seen microsoft azure ad open a browser window for login that seems to close. but i have only done that with a web app so far. i wonder how they manage that closing the tab?

Thank you for this update about your progress :raised_hands:
Related to closing the browser window/tab, this looks to be an issue with the Web Authenticator component running on Windows

also on android.
on my phone the browser window hides in the back but is still there until i close it.

on windows desktop its just easy to see it.

also in doing updates to the samples support Swashbuckle / nsawag please.
OpenAPI documentation pages are a great help in testing and documenting web api’s.

the default dotnet core 8 web api sample from Microsoft has it in the default.

i had an api working with it untill i started to add the api protection and now i am getting a CORS error… not sure why right now.

did not have the error till i added the packages to add the authorization / authentication support.

Hey @andrea.chiarelli, great post, although there is one think that i might be missing. We created RefreshToken method on MainPage but we do not use it anywhere and in delegating handler we use native await auth0Client.RefreshTokenAsync(refreshToken) without storing new tokens so i guess when refresh token rotation is enabled this just going to stop work after first refresh. Am I wrong on this one?

Hey @bpsc-wkubis,
Thank you so much for reporting this issue. Looks like it was a remaining part of the previous version of this article. I fixed it.
Thank you! :pray:

No problem :slight_smile:

I also have another 2 questions:

  1. Do we really need to validate id token every time we fetch it from secure storage? Isn’t validating it after receving from oidc server enough? Is it because someone can get access to keys in android or keychain on IOS? Follow up question, would you recommend that on web too, when we getting id token from cookies (although key ring that IDataProtection api is using is stored on server so it’s not accesible that easily)?

  2. I’m using id token for my authentication so, user is logged in as long as id token has not expired, I guess this is how it supposed to be, but some examples of auth in blazor including microsoft one is using access token expiration date as indicator when to refresh tokens during authentication verificaion (ex. OnValidatePrincipal event), do you have any idea why is that? Is there hidden meaning behind that approach? Microsoft example

Well, not easy questions. I’ll try to do my best :slightly_smiling_face:

  1. It’s a good practice to validate tokens when you get them from outside your app. You don’t know what happened to them while they were out of your control.
    You mention “when we getting id token from cookies”. I’m not sure I correctly understand this. In what scenario are you storing an ID token in a cookie? What application type? A SPA?
    In general, you shouldn’t store a token in a cookie. In fact, you shouldn’t store a token anywhere when the application is a SPA. Take a look at this article to learn more.

  2. An ID token tells you that a user has been successfully authenticated. It optionally contains info about the user. As stated in the OIDC specs, “the ID Token expiration time is unrelated the lifetime of the authenticated session between the RP and the OP”. In other words, the ID token expiration time is unrelated to the authenticated session lifetime.
    You might think that you can use the access token’s expiration time as a reference for the session length and to decide to refresh the tokens, but there are some considerations to take into account:

    • As with the ID token, the access token’s expiration time tells you that the authorization info it contains are no longer valid. You can decide to terminate your authenticated session now or request a new access token or do whatever. It’s up to you and your session management policy.
    • The biggest problem is: how can you be sure that the access token has expired? On the client side, you cannot. The access token (and its format) is an agreement between the authorization server and the resource server (typically, the API). The client should NEVER inspect an access token, because its format can change without notice.
      The only way to know that an access token has expired should be after you have used it and got an “access denied” response from the API.
      Not sure about the Microsoft example. Honestly, I don’t see the need to store the access token in the cookie (see the article I mentioned earlier) :thinking:
      If you are interested in dealing with ID and access tokens in Blazor (.NET 8), you can read this article for adding authentication and a new one about calling protected APIs will be published tomorrow.

For more details on ID and access tokens, you can read this article.
For the relationship between sessions and tokens, you can read this article

I hope this helps.