DEV Community

Cover image for Support two authentication providers with .NET 5
Ivan Pesenti
Ivan Pesenti

Posted on

Support two authentication providers with .NET 5

Hi folks 👋🏻! I'm super happy to have you here for the last episode of the series about Authentication in .NET 5.

This post will be our icing on the cake 🍰.

🔴IMPORTANT❗🔴: this post heavily relies on stuff made in the previous two episodes. So I strongly encourage you to checkout the links above and then jump back here again.

As always, if u get in trouble in following this tutorial u can check the final solution in GitHub at this link.

Rewind the tape 🔙

Up to now, we have built out what follows:

  1. A Google Firebase proj (create with Google Developer console) with a test user who can sign-in with username and password. You can open the Google console with this link
  2. Built a web api proj that makes use of token-based authentication with Jwt format
  3. Connected our web api with the Firebase proj so users can access protected resources (after a successful sign-in on Firebase)
  4. Implemented our custom sign-in mechanism by exposing an endpoint to issue a Jwt token to users

👀NOTE👀: if you've not followed the previous posts but you have a similar solution to the one presented here (maybe with different auth providers) the following approach still works for you even if you probably have to make some adjustments.

Final challenge 🎯

During this post we're going to change our web api in order to support two authentication providers at the same time. What this means is that a user can choice how to sign-in against our web api. The user could authenticates himself against Google Firebase or against our custom endpoint. If the sign-in phase is successful the user will got back a valid Jwt token which can be used to access our protected resources. As before, if the sign-in is not successful he'll got back an error response and cannot access our super-secure resources 😎.

Requirements

To follow this post on your machine you must have installed the following:

  1. NET 5 Runtime. You can download from here
  2. Visual Studio Code (you can use another IDE if you wish). Download can be found here
  3. Postman (you can use another program to consume REST-api if you wish). Download can be found here
  4. C# extension for VSCode (powered by Omnisharp)

Let's start 🚀

To complete the task we've to carry about two sections: the settings and authentication's registration. After these tasks we'll do the final test.

Check settings 🔧

First open up the "appsettings.json" file and check that you have an identical structure like me (obviously replace the value with yours):

"Jwt": {
    "Firebase": {
      "ValidIssuer": "https://securetoken.google.com/auth-series",
      "ValidAudience": "auth-series"
    },
    "AuthDemo": {
      "Key": "This is where you should specify your secret key, which is used to sign and verify Jwt tokens.",
      "ValidIssuer": "localhost:5001",
      "ValidAudience": "localhost:5001"
    }
  }
Enter fullscreen mode Exit fullscreen mode

⚠️WARNING⚠️: in a real-world app you must not store these sensitive data in a non-secure location such as appsettings.json. Moreover the key should not be so easy to guess 😋.

Here you see that we're going to support two authentication providers: "Firebase" and "AuthDemo" (that is our custom endpoint).

Providers Registration 🎙

The next file you have to open is "Startup.cs" and look at the ConfigureServices() method.

After the Firebase registration you have to adjust the code in a way that looks similar to the one below:

    ...
    services.AddTransient<ITokenService, TokenService>();

    // firebase auth
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer("Firebase", opt =>
    {
        opt.Authority = Configuration["Jwt:Firebase:ValidIssuer"];
        opt.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Firebase:ValidIssuer"],
            ValidAudience = Configuration["Jwt:Firebase:ValidAudience"]
        };
    });

    // auth demo
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer("AuthDemo", opt =>
    {
        opt.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:AuthDemo:ValidIssuer"],
            ValidAudience = Configuration["Jwt:AuthDemo:ValidAudience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:AuthDemo:Key"]))
        };
    });
Enter fullscreen mode Exit fullscreen mode

The main change in this code is that we're going to use another overload of the method AddJwtBearer. This overload accepts as the first parameter the authentication schema name which is used to identify the providers uniquely.

🧐 NOTE 🧐: you should not read the settings in this way as it's not strongly-typed and so it's a more error-prone method. Consider using the Options pattern. More on this here.

Add policy to manage multiple schemas 📜

The last step left is to add a policy to our web api that allow it to manage multiple authentication schemas at the same time. This policy must be written right below the code of the previous section. So open up "Startup.cs" and locate the "ConfigureServices()" method and write the code:

services.AddAuthorization(opt =>
{
    opt.DefaultPolicy = new AuthorizationPolicyBuilder()
    .AddAuthenticationSchemes("Firebase", "AuthDemo")
    .RequireAuthenticatedUser()
    .Build();
});
Enter fullscreen mode Exit fullscreen mode

This code snippet is self-explanatory. Its purposes are to register the schemas declared above as you should notice from the command .AddAuthenticationSchemes("Firebase", "AuthDemo") and to require the users to be authenticated in order to access our resources.

Final Test 🐲

We've finished the coding phase so we can finally give a try to our work ⚒. In your terminal go into the folder where it's contained the .csproj file.

Issue a dotnet run command and wait for the web api to start properly.

Postman

To test our software we need of three requests (that you can build by following the recipes from the previous posts). Below you can find the three requests with their titles and purposes:

  1. FirebaseSignIn: used to sign-in a user in Firebase platform
  2. AuthDemoSignIn: used to sign-in a user against our custom endpoint
  3. WeatherForecast: used to access our restricted resource

Foremost, try to execute the WeatherForecast without authentication configured:

Postman request without authentication

You should get back a 401 Unauthorized error.

Now execute the FirebaseSignIn request and copy the "idToken" value returned. Then switch back to the WeatherForecast request and change the authorization type to "Bearer Token" and paste in the token as you can see below:

Postman request with Bearer authentication

Execute again this request and check if you receive a 200 OK response together with the requested data.

Now open the AuthDemoSignIn request and execute it to get back the Jwt token. Copy the returned token.
Switch back to WeatherForecast request and replace the Firebase token with the latter one. Execute again and now it should works as expected 🌟!

Final thoughts 💭

Now we reached the end 🏁. During this series you learned a bunch of things about authentication 🔐 within .NET 5. To show off these capabilities we make use of web api project template but the same still applies to other projs such as MVC or Single Page Application.

Supporting multiple authentication providers could be a transient phase such as when you're migrating from one auth provider to another or permanent as you would like to let users decide which authentication provider utilize.

In both cases this series will provide you some guidelines about the steps you have to follow.

Greetings 👋🏻

Now, it's time to say goodbye 😄.

🔵IMPORTANT🔵: remember that authentication is one CRUCIAL aspect of software development, so don't joke with it as you'll pay the consequences for sure 🤕.

I hope you enjoy this post and find it useful. If you have any questions or you want to spot me some errors I really appreciate it and I'll make my best to follow up. If you enjoy it and would like to sustain me consider giving a like and sharing on your favorite socials. If u want u can add me on your socials this makes me very very happy!

Stay safe and see you soon! 😎

Top comments (5)

Collapse
 
jboada profile image
jboada

Excellent Article!!

Pretty simple and straightforward the explanaition of all the process to use Firebase Authentication!

I would like to handle the SignUp/Registration thru a Custom WebAPI:

  • The front end sends all the data to the WebAPI.
  • Then the WebAPI sends the data to Firebase.
  • Firebase registers the user and sends the verification email to the user.
  • Then when the user tries to login, the WebAPI could verify if the user is verified, if the user is, the WebAPI generates the Firebase token, otherwise, the WebAPI sends back a 401 Unathorize error "Please verify your email".

Do you know how to add users to Firebase Authentication thru a ASP.Net Core Web API?

I am pretty new about using an external authentication provider, I have used many custom providers from many companies in the past. I have googled a lot looking for: first, understad the idea and the process itself, second: examples of code.

Best Regards,
Juan Boada

Collapse
 
ossan profile image
Ivan Pesenti

Very glad you enjoyed my post!

In order to add users into Firebase you must use a Nuget package called "Firebase.Admin" which allow you to register your users into Firebase platform.

On your application db you could store information about your users such as email, first name, last name, email verified and FIREBASE_UID.
Whenever a user tries to access your restricted web api you could double check into your application db and in Firebase.
If you want to send a reset password email you could do it by using the same Nuget package (that you must install and configure in your backend server).

Suggestion: use the client sdk to authenticate a user direcly into Firebase and then attach the received JWT token to all subsequent requests made to your backend server. The latter is in charge of verifying the token and process the request or return the unauthorized error 401.

Hope this clarify a bit the matter!

Collapse
 
jboada profile image
jboada

Hi Ivan,

Thank you for comments, I have been reviewing the package. You gave some really nice help to work with it.

Best Regards,
JB

Collapse
 
pedrostefanogv profile image
Pedro Stéfano

Faz um artigo sobre como gerar token personalizados com roles e clains

Ficou muito bom o seu artigo, simples e fácil de compreender

Collapse
 
ossan profile image
Ivan Pesenti

Thank you Pedro :-)