Skip to content

Commit

Permalink
Merge branch 'main' into Add-Separate-Messages
Browse files Browse the repository at this point in the history
  • Loading branch information
JackDevAU committed Nov 30, 2023
2 parents aa6996b + 38e9f86 commit 84be89a
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/validate-markdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

- name: Filter and format Markdown files
run: |
MARKDOWN_FILES=$(echo '${{ steps.files.outputs.all }}' | jq -r '.[] | select(endswith(".md"))')
MARKDOWN_FILES=$(echo '${{ steps.files.outputs.all }}' | jq -r '.[] | select(endswith(".md"))' | tr '\n' ' ')
echo "MARKDOWN_FILES=$MARKDOWN_FILES" >> $GITHUB_ENV
- name: Install markdownlint-cli
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ index:
- do-you-use-trace-fail-or-set-assertuienabled-true-in-your-web-config
- dev-containers
- containerize-sql-server
- minimal-apis

---
If you still need help, visit [ASP.NET MVC Web Application Development](http://www.ssw.com.au/ssw/Consulting/WebsiteDevelopment.aspx) and book in a consultant.
Expand Down
60 changes: 29 additions & 31 deletions rules/integrate-identityserver-with-an-existing-user-store/rule.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,84 @@
---
type: rule
title: Do you know how to migrate an existing user store to an ExternalAuthProvider?
uri: migrate-an-existing-user-store-to-externalauthprovider
uri: integrate-identityserver-with-an-existing-user-store
authors:
- title: "Dhruv Mathur"
url: https://www.ssw.com.au/people/dhruv/
created: 2023-10-31T04:31:12.396Z
guid: 38a5988b-1740-4120-840d-116ad6e91566
---
When integrating an external authentication provider (IdentityServer, Azure AD or Microsoft Entra ID etc.) with an existing ASP .NET Core application which uses ASP .NET Core Identity, challenges arise due to different user identification systems.
When integrating an external authentication provider (IdentityServer, Azure AD or Microsoft Entra ID etc.) with an existing ASP .NET Core application which uses ASP .NET Core Identity, challenges arise due to different user identification systems.

On the ExternalAuthProvider side, users are typically recognised by a unique SubId within their issued token after authentication. In contrast, an application's existing user store might use its own unique user ID, possibly combined with other data.
On the ExternalAuthProvider side, users are typically recognised by a unique SubId within their issued token after authentication. In contrast, an application's existing user store might use its own unique user ID, possibly combined with other data.

The above discrepancy creates the need to effectively map or correlate the the user with SubId from the ExternalAuthProvider to the corresponding user with user ID within the app's user store.

## Two essential scenarios arise when integrating the ExternalAuthProvider:
Two essential scenarios arise when integrating the ExternalAuthProvider:

### 1. Pre-existing Company Users
## 1. Pre-existing Company Users

#### **Technical Integration & Benefits**
Addressing users already registered with company emails in the existing application user store now authenticating through the ExternalAuthProvider:

**Addressing users already registered with company emails in the existing application user store now authenticating through the ExternalAuthProvider:**
**SubId Check**:

* **SubId Check**:

* Begin by verifying if the user has an external login associated with the SubId from the ExternalAuthProvider in your application's user store using the [`FindByLoginAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.findbyloginasync?view=aspnetcore-7.0) method. If found, proceed with authentication by handling the request gracefully.
* Begin by verifying if the user has an external login associated with the SubId from the ExternalAuthProvider in your application's user store using the [`FindByLoginAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.findbyloginasync?view=aspnetcore-7.0) method. If found, proceed with authentication by handling the request gracefully.

```csharp
var existingUserByExternalLogin = await _userManager.FindByLoginAsync(EXTERNAL_AUTH_PROVIDER, subId);
```

* **Existing Users by Email Verification**:
**Existing Users by Email Verification**:

* If there's no associated SubId, check if the email (provided by the ExternalAuthProvider during authentication available as one of the claims in the JWT token) exists in your application's user store.
* If there's no associated SubId, check if the email (provided by the ExternalAuthProvider during authentication available as one of the claims in the JWT token) exists in your application's user store.

```csharp
var userByUserName = await _userManager.FindByEmailAsync(emailFromIdentityServer);
```

::: good
Figure: Retrieving existing user by using the Email claim from the JWT token utilising the FindByEmailAsync() from the UserManager class.
:::
* For users known to your application but not authenticated via the ExternalAuthProvider:

* Retrieve the SubId from the JWT token provided by the ExternalAuthProvider.
* Use ASP .NET Core Identity's [`AddLoginAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.addloginasync?view=aspnetcore-8.0) method to associate this SubId as an external login with the user's record.
**For users known to your application but not authenticated via the ExternalAuthProvider**:

* Retrieve the SubId from the JWT token provided by the ExternalAuthProvider.
* Use ASP .NET Core Identity's [`AddLoginAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.addloginasync?view=aspnetcore-8.0) method to associate this SubId as an external login with the user's record.

```csharp
var subId = token.Claims.FirstOrDefault(c => c.Type == "sub");
await _userManager.AddLoginAsync(newUser, new UserLoginInfo(EXTERNAL_AUTH_PROVIDER, subId));
```

::: greybox
Note: In the example above the "EXTERNAL\_AUTH\_PROVIDER" is a constant which contains the identifier for your external authentication provider. e.g. IDENTITY\_SERVER\_EXTERNAL\_LOGIN = "IdentityServer"
:::

* **Future Authentications**:
**Future Authentications**:

* For all subsequent logins, employ the [`FindAsync(new UserLoginInfo())`](https://learn.microsoft.com/en-us/previous-versions/aspnet/dn497605(v=vs.108)) method.
* For all subsequent logins, employ the [`FindAsync(new UserLoginInfo())`](https://learn.microsoft.com/en-us/previous-versions/aspnet/dn497605(v=vs.108)) method.

**Benefits**:
**Benefits**:

* Seamless authentication experience for existing users.
* Avoids custom fields in the ApplicationUser model, leveraging existing ASP .NET Identity Core methods like [`AddLoginAsync`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.addloginasync?view=aspnetcore-8.0) and [`FindAsync`](https://learn.microsoft.com/en-us/previous-versions/aspnet/dn497605(v=vs.108)).

### 2. New ExternalAuthProvider Registrants
## 2. New ExternalAuthProvider Registrants

#### **Technical Integration & Benefits**
Incorporating users who are new to both the ExternalAuthProvider and the application:

**Incorporating users who are new to both the ExternalAuthProvider and the application:**
**Email Verification**:

* **Email Verification**:

* Check if the email provided by the ExternalAuthProvider during authentication exists in your application's user store by using ASP .NET Identity Core methods like [`FindByEmailAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.findbyemailasync?view=aspnetcore-7.0).
* If it doesn't, this indicates the user is new.
* Check if the email provided by the ExternalAuthProvider during authentication exists in your application's user store by using ASP .NET Identity Core methods like [`FindByEmailAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.findbyemailasync?view=aspnetcore-7.0).
* If it doesn't, this indicates the user is new.

```csharp
var existingUser = await _userManager.FindByEmailAsync(emailFromIdentityServer);
```

**User Creation & SubId Association**:


* **User Creation & SubId Association**:

* Register by creating a new user in your application's store using claims provided by the ExternalAuthProvider (e.g., first name, last name, email).
* Register by creating a new user in your application's store using claims provided by the ExternalAuthProvider (e.g., first name, last name, email).

```csharp
var newUser = new Domain.Entities.ApplicationUser
Expand All @@ -107,9 +104,10 @@ Note: In the example above extraction of claims may vary based on how you access
var subId = token.Claims.FirstOrDefault(c => c.Type == "sub");
await _userManager.AddLoginAsync(newUser, new UserLoginInfo(EXTERNAL_AUTH_PROVIDER, subId));
```
* **Future Authentications**:

* Finally for all subsequent logins use the [`FindByLoginAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.findbyloginasync?view=aspnetcore-7.0) method to check if the user already exists.
**Future Authentications**:

* Finally for all subsequent logins use the [`FindByLoginAsync()`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.findbyloginasync?view=aspnetcore-7.0) method to check if the user already exists.

```csharp
var existingUser = await _userManager.FindByLoginAsync(EXTERNAL_AUTH_PROVIDER, subId));
Expand Down
55 changes: 55 additions & 0 deletions rules/minimal-apis/rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
type: rule
title: Do you use Minimal APIs over Controllers?
uri: minimal-apis
authors:
- title: Daniel Mackay
url: https://ssw.com.au/people/daniel-mackay
- title: William Liebenberg
url: https://ssw.com.au/people/william-liebenberg
- title: Brady Stroud
url: https://www.ssw.com.au/people/brady-stroud
related: []
redirects: []
created: 2023-12-01T02:55:57.0000000Z
archivedreason: null
guid: 300ef178-e0b9-43bb-bb92-3076253005b1
---

Traditional controllers require a lot of boilerplate code to set up and configure. Most of the time your endpoints will be simple and just point to a mediator handler.

Minimal APIs are a simplified approach for building fast HTTP APIs with ASP.NET Core. You can build fully functioning REST endpoints with minimal code and configuration. Skip traditional scaffolding and avoid unnecessary controllers by fluently declaring API routes and actions.

Check out the Microsoft Docs for more information on [Minimal APIs](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis).

<!--endintro-->

```csharp
[ApiController]
[Route("[controller]")]
public class HelloWorldController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("Hello World!");
}
}
```
::: bad
Figure: Bad Example - 9 lines of code for a simple endpoint
:::

```csharp
app.MapGet("/", () => "Hello World!");
```
::: good
Figure: Good Example - 1 line of code for a simple endpoint
:::

Minimal APIs are great for
- Learning
- Quick prototypes
- Vertical Slice Architecture
- A similar developer experience to NodeJS
- Performance

0 comments on commit 84be89a

Please sign in to comment.