IdentityServer4,ASP.NET Identity for Authentication & Authorization with ReactJS client

bazen

Bazen

Posted on February 20, 2021

IdentityServer4,ASP.NET Identity for Authentication & Authorization with ReactJS client

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 :- for authentication & authorization
2) ASP.NET Identity :- User information storage
3) .NET API :- API protected by IdentityServer4
4) React :- 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.to keep things simple I'm going to use one of the templates by running the following commands.

dotnet new -i identityserver4.templates
Enter fullscreen mode Exit fullscreen mode

To see the installed templates run

dotnet new -l
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
        }
    }
};
Enter fullscreen mode Exit fullscreen mode

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,
            },
    };
}
Enter fullscreen mode Exit fullscreen mode

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>();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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";
     });
Enter fullscreen mode Exit fullscreen mode

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" })
}
Enter fullscreen mode Exit fullscreen mode

Next step is to create & setup our react app by running the following commands:

npx create-react-app sample-spa --template typescript
Enter fullscreen mode Exit fullscreen mode

Or

yarn create react-app my-app --template typescript
Enter fullscreen mode Exit fullscreen mode

After creating the react app. we will add the best oidc library called oidc-react which is the best oidc library I have seen by far.

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);
  }
};

Enter fullscreen mode Exit fullscreen mode

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>        
Enter fullscreen mode Exit fullscreen mode

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>
   )
}   
Enter fullscreen mode Exit fullscreen mode

Now we are done. I hope you enjoyed this.
Thanks for reading

Happy Coding!!!

💖 💪 🙅 🚩
bazen
Bazen

Posted on February 20, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related