IdentityServer4,ASP.NET Identity for Authentication & Authorization with ReactJS client
Tags: dotnet, react, typescript, security
Published: 2021-02-20 21:05:03

Very recently I had a scenario where I have to implement two web apps. for both apps I had to implement authentication and authorization. I spent quite sometime researching on what is the better way to implement this & I hope this saves your time for anyone interested. anyway after doing my research I decided to use the following technologies...

1) **[IdentityServer4](https://identityserver.io/)**  :- for authentication & authorization
2) **[ASP.NET Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-5.0&tabs=visual-studio)** :- User information storage 
3) **[.NET API](https://dotnet.microsoft.com/apps/aspnet/apis)**         :- API protected by IdentityServer4
4) **[React](https://reactjs.org/)** :- React & Typescript Client App that is going to consume API  

Lets start coding...

**Step 1: Identity Server**
We can either create empty project and do all the work by our self or we can use the one of the [IdentityServer4 templates](https://github.com/IdentityServer/IdentityServer4.Templates).to keep things simple I'm going to use one of the templates by running the following commands.

```
dotnet new -i identityserver4.templates
```   
To see the installed templates run
```
dotnet new -l
```
There are couple of template options to choose from.in this case we want to use ASP.NET Identity as the user data storage so we will run this command
```
dotnet new is4aspid
```
now that we have our project ready its time to edit some of the code. find `Config.cs` file which contains the identity configuration. the first this we are going to do is to add the Api resource
```
public static IEnumerable<ApiResource> ApiResources
=> new ApiResource[]
   { new ApiResource("sample.api","Sample Api") 
     { 
       Scopes = new string[] 
               { ProtectedApiScopes.ScannerApiScope.Name}}
      };
``` 
next step is to add our Client SPA app to the clients list
```
public static IEnumerable<Client> Clients =>
new Client[]
{
   new Client
   {
      ClientId = "sample.spa",
      ClientName = "Sample SPA",
      AllowedGrantTypes = GrantTypes.Implicit,
      AllowAccessTokensViaBrowser = true,
      RequireConsent = false,
      AccessTokenLifetime = 120,
      RedirectUris = { "http://localhost:3000"},
      PostLogoutRedirectUris ={"http://localhost:3000"},
       AllowedCorsOrigins = { "http://localhost:3000" },
       AllowedScopes = {
           IdentityServerConstants.StandardScopes.OpenId,
           IdentityServerConstants.StandardScopes.Profile,
       },
 };
```
This is enough change for the identity configuration, now we have to add our configuration to `IServiceCollection` in `StartUp.cs` as follows
```
services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryPersistedGrants()             
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<ApplicationUser>();
``` 
In production its better to store your identity server configuration in the database, but lets keep things simple for now.we are done configuring the identity server, next step is to create & setup the .NET api project.project can be created by running the following command 

```
dotnet new webapi
```
after creating the project we have to add reference to the `IdentityServer4.AccessTokenValidation` package.we can then add configuration in the `StartUp.cs` file by adding the following code
```
 services.AddAuthentication("Bearer")
         .AddIdentityServerAuthentication(options =>
         {
             options.Authority = "http://localhost:5000";//Identity Server Uri
             options.RequireHttpsMetadata = false;
             options.ApiName = "sample.api";
          });  
```
That's it now we can simply guard any end point by adding `[Authorize]` attribute on top of it
```
[Authorize]
[HttpGet]
public IActionResult Get()
{
   return Ok(new { Message= "You are authenticated" })
}
```
next step is to create & setup our react app.
> npx create-react-app sample-spa --template typescript
Or
> yarn create react-app my-app --template typescript

After creating the react app. we will add the best oidc library called [oidc-react](https://www.npmjs.com/package/oidc-react) which is the best oidc library I have seen by far & I hope you will come why.
> npm install oidc-react
create `oidc-config.ts` file in the src directory of sample-spa project and add the following configuration
```
export const customOidcConfig: AuthProviderProps = {
  clientId: 'sample.spa',
  automaticSilentRenew: true,
  redirectUri: 'http://localhost:3000/',
  responseType: 'token id_token',
  scope:"openid profile",
  authority: 'http://localhost:5000',
  onBeforeSignIn:()=>{
     /**
      * This method gets executed before signin redirecting begins
      * it can be used to store Uri
      */
  }
  onSignIn:async (user: User | null)=>{
      console.log('PATH',window.location.pathname)
        if(user){
            console.group('[ LOGIN: SUCCESS ]', user);
         };
         window.location.hash = '';
    }
    else{
       console.error('[ LOGIN: ERRNO ]')
    }
  },
  onSignOut:async (options?: AuthProviderSignOutProps) =>{
      console.log('[ SignOutOpts ]', options);
  }
};

```
next step is to initiate login using the configuration above. find `App.tsx` file and update it using the following code

```
<AuthProvider {...customOidcConfig}>
   <AuthCheck/>          
</AuthProvider>
              
``` 
This will automatically initiate the login process. we can also check if the user is logged in by using `useAuth` hook.
```
export const AuthCheck: FC =() =>{
   const authState = useAuth();
   return (
     <Fragment>
        { 
          authState && 
          authState.userData && 
          <p>Authenticated</p> 
        }
        {
          (!authState ||
          !authState.userData) && 
          <p>Not Authenticated</p>
        }
     </Fragment>
   )
}   
```  
yeah, now we are done. I hope you enjoyed this. 
Thanks for reading

Happy Coding!!!