Skip to content

Commit

Permalink
XS✔ ◾ Update Rule - bug fixes and semantic changes (#7367)
Browse files Browse the repository at this point in the history
* Update rule.md

* Auto-fix Markdown files

* semantic changes for better readability

* Auto-fix Markdown files

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
Dhruv-0987 and github-actions[bot] authored Nov 30, 2023
1 parent debff34 commit 38e9f86
Showing 1 changed file with 29 additions and 31 deletions.
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

0 comments on commit 38e9f86

Please sign in to comment.