Skip to content

Commit

Permalink
More polish on external auth
Browse files Browse the repository at this point in the history
- Rename scheme to provider
- Added docs on how to configure auth0
  • Loading branch information
davidfowl committed Dec 18, 2022
1 parent 3dceedd commit 1c559c2
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 4 deletions.
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,80 @@ Authentication__Schemes__<scheme>__ClientId=xxx
Authentication__Schemes__<scheme>__ClientSecret=xxx
```

Or using user secrets:

```
dotnet user-secrets set Authentication:Schemes:<scheme>:ClientId xxx
dotnet user-secrets set Authentication:Schemes:<scheme>:ClientSecret xxx
```

Other providers can be found [here](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers#providers).
These must be added to [AuthenticationExtensions](Todo.Web/Server/Authentication/AuthenticationExtensions.cs) as well.

**NOTE: Don't store client secrets in configuration!**

### External Authentication servers

Much like social authentication, this application also supports external [Open ID connect (OIDC) servers](https://en.wikipedia.org/wiki/OpenID). External authentication
is treated like social authentication provider but that also produce access tokens that can be used with the [TodoAPI](TodoAPI). This
needs to be configured like a social provider in the [Todo.Web.Server](Todo.Web/Server) application and an additional authentication scheme
needs to be configured in the [TodoAPI](TodoAPI) to accept JWT tokens issued by the auth server.

Here's what the flow looks like:

![image](https://user-images.githubusercontent.com/95136/208310479-808ea1ed-db48-49d1-b466-3ba33a08bcbc.png)

Here's how you would configure authentication:

```JSON
{
"Authentication": {
"Schemes": {
"<scheme>": {
}
}
}
}
```

**NOTE: Don't store client secrets in configuration!**

#### Auth0

This sample has **Auth0** configured as an OIDC server. It can be configured with the following schema:

```JSON
{
"Authentication": {
"Schemes": {
"Auth0": {
"Audience": "<audience>",
"Domain": "<domain>",
"ClientId": "<client id>",
"ClientSecret": "<client secret>"
}
}
}
}
```

Here's an example of configuring the [TodoAPI](TodoAPI):

```JSON
{
"Authentication": {
"Schemes": {
"Auth0": {
"ValidAudiences": [ "<your audience here>" ],
"Authority": "<your authority here>"
}
}
}
}
```

Learn more about the Auth0 .NET SDK [here](https://github.com/auth0/auth0-aspnetcore-authentication).

### OpenTelemetry
TodoApi uses OpenTelemetry to collect logs, metrics and spans.

Expand Down
13 changes: 12 additions & 1 deletion TodoApi/Authentication/AuthenticationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ public static WebApplicationBuilder AddAuthentication(this WebApplicationBuilder

if (section.Exists())
{
// This is what the auth0 configuration looks like:
//{
// "Authentication": {
// "Schemes": {
// "Auth0": {
// "ValidAudiences": [ "<your audience here>" ],
// "Authority": "<your authority here>"
// }
// }
// }
//}
authenticationBuilder.AddJwtBearer("Auth0", options =>
{
options.Events = new()
Expand All @@ -40,7 +51,7 @@ public static WebApplicationBuilder AddAuthentication(this WebApplicationBuilder
if (context.Principal?.Identity is ClaimsIdentity identity)
{
// Add this claim in memory so that we can look up the user by provider name
identity.AddClaim(new("scheme", "Auth0"));
identity.AddClaim(new("provider", "Auth0"));
}
return Task.CompletedTask;
Expand Down
6 changes: 3 additions & 3 deletions TodoApi/Authorization/CurrentUserExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
// to set the current user without adding custom middleware.
_currentUser.Principal = principal;

var scheme = principal.FindFirstValue("scheme");
var loginProvider = principal.FindFirstValue("provider");

if (principal.FindFirstValue(ClaimTypes.NameIdentifier) is { Length: > 0 } name)
{
// Resolve the user manager and see if the current user is a valid user in the database
// we do this once and store it on the current user.
_currentUser.User = scheme is null
_currentUser.User = loginProvider is null
? await _userManager.FindByNameAsync(name)
: await _userManager.FindByLoginAsync(scheme, name);
: await _userManager.FindByLoginAsync(loginProvider, name);
}

return principal;
Expand Down

0 comments on commit 1c559c2

Please sign in to comment.