-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d502b80
commit 7640cbc
Showing
10 changed files
with
704 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
+++ | ||
title = "Authentication" | ||
weight = 30 | ||
+++ | ||
|
||
Most of the time, you will want to use bearer authentication so that you can use a JWT (Json Web Token) obtained from an OIDC server. This is so prevalent that we provide an easy mechanism to add this to your application via a `GenericAuthenticationProvider`. The authentication provider only requests tokens from your token retrieval method when required (when the provided token is close to expiring or has expired). | ||
|
||
The `GenericAuthenticationProvider` and associated classes are in the `CommunityToolkit.Datasync.Client.Authentication` namespace. | ||
|
||
## Set up authentication and authorization on the datasync service | ||
|
||
You must set up authentication and authorization on the datasync service first. The authentication and authorization is regular ASP.NET Core | ||
identity, so [follow the instructions](https://learn.microsoft.com/aspnet/core/security/) for your particular provider. | ||
|
||
## Create a method to retrieve the token | ||
|
||
You need to implement a method to retrieve the token. Normally, this uses the library that is provided for the purpose. For example: | ||
|
||
* Microsoft logins use [Microsoft.Identity.Client](https://www.nuget.org/packages/Microsoft.Identity.Client). | ||
* Other logins on MAUI may use [WebAuthenticator](https://learn.microsoft.com/dotnet/maui/platform-integration/communication/authentication) | ||
|
||
Whatever mechanism you use, this must be set up first. If your application is unable to get a token, the authentication middleware cannot pass it onto the server. | ||
|
||
## Add the GenericAuthenticationProvider to your client | ||
|
||
The `GenericAuthenticationProvider` takes a function that retrieves the token. For example: | ||
|
||
```csharp | ||
public async Task<AuthenticationToken> GetTokenAsync(CancellationToken cancellationToken = default) | ||
{ | ||
// Put the logic to retrieve the JWT here. | ||
DateTimeOffset expiresOn = expiry-date; | ||
return new AuthenticationToken() | ||
{ | ||
Token = "the JWT you need to pass to the service", | ||
UserId = "the user ID", | ||
DisplayName = "the display Name", | ||
ExpiresOn = expiresOn | ||
}; | ||
} | ||
``` | ||
|
||
You can now create a GenericAuthenticationProvider: | ||
|
||
```csharp | ||
GenericAuthenticationProvider authProvider = new(GetTokenAsync); | ||
``` | ||
|
||
### Build HttpClientOptions with the authentication provider | ||
|
||
The authentication provider is a `DelegatingHandler`, so it belongs in the `HttpPipeline`: | ||
|
||
```csharp | ||
HttpClientOptions options = new() | ||
{ | ||
HttpPipeline = [ authProvider ], | ||
Endpont = "https://myservice.azurewebsites.net" | ||
}; | ||
``` | ||
|
||
You can then use this options structure when constructing a client (either in the `OnDatasyncInitialization()` method or when constructing the `DatasyncServiceClient`). | ||
|
||
> [!TIP] | ||
> It's normal to inject the authentication provider as a singleton in an MVVM scenario with dependency injection. | ||
## Forcing a login request | ||
|
||
Sometimes, you want to force a login request; for example, in response to a button click. You can call `LoginAsync()` on the authentication provider to trigger a login sequence. The token will then be used until it expires. | ||
|
||
## Refresh token | ||
|
||
Most providers allow you to request a "refresh token" that can be used to silently request an access token for use in accessing the datasync service. You can store and retrieve refresh tokens from local storage in your token retrieval method. The `GenericAuthenticationProvider` does not natively handle refresh tokens for you. | ||
|
||
## Other options | ||
|
||
You can specify which header is used for authorization. For example, Azure App Service Authentication and Authorization service uses the `X-ZUMO-AUTH` header to transmit the token. This is easily set up: | ||
|
||
```csharp | ||
GenericAuthenticationProvider authProvider = new(GetTokenAsync, "X-ZUMO-AUTH"); | ||
``` | ||
|
||
Similarly, you can specify the authentication type for the authorization header (instead of Bearer): | ||
|
||
```csharp | ||
GenericAuthenticationProvider authProvider = new(GetTokenAsync, "Authorization", "Basic"); | ||
``` | ||
|
||
This gives you significant flexibility to build the authentication mechanism appropriate for your application. | ||
|
||
By default, a new token is requested if the old token is expired or within 2 minutes of expiry. You can adjust the amount of buffer time using the `RefreshBufferTimeSpan` property: | ||
|
||
```csharp | ||
GenericAuthenticationProvider authProvider = new(GetTokenAsync) | ||
{ | ||
RefreshBufferTimeSpan = TimeSpan.FromSeconds(30) | ||
}; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
src/CommunityToolkit.Datasync.Client/Authentication/AuthenticationProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
namespace CommunityToolkit.Datasync.Client.Authentication; | ||
|
||
/// <summary> | ||
/// Definition of an authentication provider, which is a specific type of delegating | ||
/// handler that handles authentication updates. | ||
/// </summary> | ||
public abstract class AuthenticationProvider : DelegatingHandler | ||
{ | ||
/// <summary> | ||
/// The display name for the currently logged in user. This may be null. | ||
/// </summary> | ||
public string? DisplayName { get; protected set; } | ||
|
||
/// <summary> | ||
/// If true, the user is logged in (and the UserId is available). | ||
/// </summary> | ||
public bool IsLoggedIn { get; protected set; } | ||
|
||
/// <summary> | ||
/// The User ID for this user. | ||
/// </summary> | ||
public string? UserId { get; protected set; } | ||
|
||
/// <summary> | ||
/// Initiate a login request out of band of the pipeline. This can be used to | ||
/// initiate the login process via a button. | ||
/// </summary> | ||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe.</param> | ||
/// <returns>An async task that resolves when the login is complete.</returns> | ||
public abstract Task LoginAsync(CancellationToken cancellationToken = default); | ||
} |
37 changes: 37 additions & 0 deletions
37
src/CommunityToolkit.Datasync.Client/Authentication/AuthenticationToken.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
namespace CommunityToolkit.Datasync.Client.Authentication; | ||
|
||
/// <summary> | ||
/// Definition of an authentication token response. | ||
/// </summary> | ||
public struct AuthenticationToken | ||
{ | ||
/// <summary> | ||
/// The display name for this user. | ||
/// </summary> | ||
public string DisplayName { get; set; } | ||
|
||
/// <summary> | ||
/// The expiry date of the JWT Token | ||
/// </summary> | ||
public DateTimeOffset ExpiresOn { get; set; } | ||
/// <summary> | ||
/// The actual JWT Token | ||
/// </summary> | ||
public string Token { get; set; } | ||
|
||
/// <summary> | ||
/// The User Id for this user | ||
/// </summary> | ||
public string UserId { get; set; } | ||
|
||
/// <summary> | ||
/// Return a visual representation of the authentication token for logging purposes. | ||
/// </summary> | ||
/// <returns>The string representation of the authentication token</returns> | ||
public override readonly string ToString() | ||
=> $"AuthenticationToken(DisplayName=\"{DisplayName}\",ExpiresOn=\"{ExpiresOn}\",Token=\"{Token}\",UserId=\"{UserId}\")"; | ||
} |
Oops, something went wrong.