Securing your single page application with Firebase and .Net WebAPI 2 authentication

I have a Single Page Application built with AngularJs (although the framework is not crucial at this stage). The application is hosted on IIS and consists of an index.html file along with various client assets.

For the backend, I am using WebApi 2, which is also hosted on IIS as a separate application.

To handle authentication on the client side, I am utilizing Firebase (simple login) with support for multiple social networks such as Facebook, Twitter, and Google.

Everything seems to be working smoothly so far. I particularly appreciate how effortless it is to enable Twitter authentication through Firebase.

Upon logging in with a social network, I receive the firebaseAuthToken and provider accessToken from Firebase.

Now, my goal is to use either the firebaseAuthToken or provider access token to authenticate with my WebApi.

The main question that arises is: What would be the most effective method for authenticating with WebApi considering these circumstances?

Unfortunately, switching solely to Firebase to store data and eliminating the need for WebApi is not an option due to the complex business logic implemented on the server.

One initial but somewhat unconventional idea I have is to transmit the social provider access token to the server, validate the token against the provider, and then generate a security token using Owin - Katana.

I haven't opted for the built-in social providers' support from Katana because of the lack of documentation, complexity, and poor integration with single page applications. Additionally, I found the Visual Studio template for SPA to be too MVC-centric. But that's just my personal preference!

Answer №1

tl;dr - Check out my Demo Project on GitHub

Although the steps may appear lengthy, setting up this demo project is actually quite straightforward. I managed to create it in just about an hour.


I share your sentiments regarding Owin and Katana. Going through that process in the past wasn't the most pleasant experience. Using Firebase made things much simpler.

All of this can be achieved with JWTs!

Upon authentication via Firebase and any social provider, a JSON Web Token (JWT) called firebaseAuthToken is returned.

Retrieve your Firebase Secret from the Dashboard

JWTs utilize a secret token and a client token. The client token, which is the firebaseAuthToken obtained upon login, works alongside the secret token generated within the Firebase Dashboard.

Store your Firebase Secret in the appSettings section of your Web.config

It's important to store this secret key in the Web.config for easy access later on.

<add key="FirebaseSecret" value="<Enter-Firebase-Secret-Token-Here" />

Create an Action Filter to validate the JWT from the Authorization Header

We can verify the validity of a request by including the client token in the Authorization header. We can also store our secret key received from the Firebase Dashboard on the server side. Upon inspection by Web API, we decode the JWT with the help of a JWT Library (available from NuGet). In case of successful decoding, we check if the token has expired.

public class DecodeJWT: ActionFilterAttribute 
{

    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) 
    {
        string firebaseAuthToken = string.Empty;
        if (actionContext.Request.Headers.Authorization != null) {
            firebaseAuthToken = actionContext.Request.Headers.Authorization.Scheme;
        } else {
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        }

        string secretKey = WebConfigurationManager.AppSettings["FirebaseSecret"];
        try {
            string jsonPayload = JWT.JsonWebToken.Decode(firebaseAuthToken, secretKey);
            DecodedToken decodedToken = JsonConvert.DeserializeObject < DecodedToken > (jsonPayload);
            // To-do: Check expiry of decoded token
        } catch (JWT.SignatureVerificationException jwtEx) {
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        } catch (Exception ex) {
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        }

        base.OnActionExecuting(actionContext);
    }

}

Create a $httpInterceptor to append the firebaseAuthToken to the header for each request

On the client side, the token must be passed every time. To simplify this process, we create a $httpInterceptor using Angular, which checks for a firebaseAuthToken stored in sessionStorage.

.factory('authInterceptor', function ($rootScope, $q, $window) {
    return {
        request: function (config) {
            config.headers = config.headers || {};
            if ($window.sessionStorage.firebaseAuthToken) {
                config.headers.Authorization = $window.sessionStorage.firebaseAuthToken;
            }
            return config;
        },
        response: function (response) {
            if (response.status === 401) {
                // To-do: User is not authenticated
            }
            return response || $q.when(response);
        }
    };
})

Assign the firebaseAuthToken to sessionStorage upon successful login

Whenever a user logs in, we can assign the value to sessionStorage.

$rootScope.$on('$firebaseSimpleLogin:login',
    function (e, user) {

        // Add a cookie for the auth token
        if (user) {
            $window.sessionStorage.firebaseAuthToken = user.firebaseAuthToken;
        }

        cb(e, user);
    });

Register the DecodeJWT filter globally

In the WebApiConfig.cs Register method, we can set the DecodeJWT filter to apply to all our ApiControllers.

config.Filters.Add(new DecodeJWT());

Now, whenever a request is made to an ApiController, it will only proceed if a valid JWT is provided. This allows us to save user data to an ApiController after they log in, assuming the data doesn't already exist.

// Globally uses DecodeJWT
public class UsersController: ApiController 
{
    // POST api/users
    public void Post([FromBody] FbUser user) // Refer to GitHub for this Model
    {
        // Save user if it does not already exist
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to run a PHP script in Firebase?

I'm facing a unique scenario where I have 10 iOS apps that offer subscription in-app purchases. I am looking to make one subscription purchase valid across all 10 apps, which requires server-side receipt validation. The process involves sending the re ...

Tips for properly integrating jQuery with knockout.js in an ASP.NET MVC5 environment

I have recently developed an MVC project using VisualStudio 2017. Update: I have upgraded knockout and jQuery to their latest versions. Everything was working fine until I attempted to use jQuery in my .js file located at the bottom of the page. Here is ...

Using NodeJs to pass values to a callback within a condition statement and retrieve the returned value

Within my routing file, I have implemented a condition where an external function is used to compare two values: obj.id === getId. Based on whether this condition evaluates to true or false, the view is rendered, or the user access is blocked. The functio ...

The function of the Selenium wait/sleep command

Can someone shed some light on why this Selenium C# code is not waiting for the elements to load properly?! It's driving me nuts! _fireFoxWebDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(1000)); As an alternative, I even tried using ...

Is there a way to transform a string that holds an array into an actual Array utilizing Javascript?

I have a string that looks like this: '[a, b]' I am looking to create an array that appears as follows: ["a", "b"] This is the script I came up with: const str = '[a, b]' const arr = str.replace('[', '').replace( ...

In Javascript, assign default values to an array and update them with new values upon the click of a

My goal is to create a quiz that populates an array. Initially, the quiz is empty but I aim to assign it a default value. This serves as my question navigation: /** * * @param {int} question * @returns {QuizPart} ...

Dealing with multiple JSON objects and organizing them into an array

After making an ajax call, I receive a response consisting of multiple JSON objects. // Sample response from ajax call: {"name":"suresh","class":"10th"},{"name":"suresh","class":"10th"} I am looking for assistance in splitting these objects and storing t ...

Is it possible to prefetch library calls in Next.js in advance?

Recently, I started working with GeoChart using react-google-charts (https://github.com/RakanNimer/react-google-charts). However, I noticed that several scripts load after the entire process is completed. In my scenario, is loading towards the end. Is t ...

Is there a way to attach a JavaScript event to a textarea without directly accessing it?

I am curious about how to incorporate the following code: onblur="hcb.watermark.blur(event)" onfocus="hcb.watermark.focus(event)" style="color: rgb(136, 136, 136); into a textarea with the id "HCB_textarea" and the class "commentbox hcb-shadow-r," withou ...

What causes the FirebaseError: 'projectId' not provided when using import/export syntax with firebase.initializeApp?

I recently downloaded a correction folder for my e-commerce project, but I'm facing some issues. When I run the 'npm start' command, I get an error in the console. It seems like the problem is not related to the Firebase object config becaus ...

Using Javascript to automatically replace content when the page is loaded

Having trouble with my fullCalendar jquery calendar and the 'eventClick' method. When I click on an event, the URL is getting encoded incorrectly. For example, I'm trying to pass a Wordpress URL like this: http://sitedomain.com/?post_type= ...

What is the method for configuring automatic text color in CKEditor?

https://i.sstatic.net/yEM3p.png Is there a way to change the default text color of CKEditor from #333333 to #000000? I have attempted to modify the contents.css in the plugin folder: body { /* Font */ font-family: sans-serif, Arial, Verdana, "Tr ...

Troubleshooting issue with changing class based on input values

It appears that there is an issue with the functionality when switching values on page load. Initially, I was able to make it work for a single switch, but now that there are multiple switches on the page, toggling affects all of them. How can I modify it ...

Using a forEach loop within an Else statement is ineffective

I've encountered an issue while trying to merge two arrays and create a new one. It seems that my forEach loop inside the else statement is returning undefined. I'm unsure if I made a mistake in my approach or if forEach is not meant to be used w ...

React.js removing one of several displayed elements

Just starting out on my todo app project. I've got the functionality where clicking the plus button adds a new task to the list. However, I'm facing an issue with the delete icon - it's deleting all tasks instead of just the one it belongs t ...

Tips for configuring field names with templates in AngularJS

I am looking to incorporate label-field interaction with field validation properties. To demonstrate this, I have created a functional example on Plunker. Visit the Plunker Example In the provided example, the field name is currently hardcoded. I attemp ...

Loading several items using identical function

Seeking a way to efficiently load multiple models and access them outside the loader, I aim to adhere to the DRY (Don't Repeat Yourself) principle by creating a single function for loading and returning the object. function loadObject(obj, mtl) { ...

Juggling various .NET languages within a web development project

Our development team is currently working on a new ASP.NET 3.5 web application. We have two C# coders and one VB.NET coder on our team. We are aware that we can mix languages on a per-project basis, allowing us to build classes in different languages that ...

Is there a way to convert a PDF file to a Javascript array using a

Is there a way to extract only words from a PDF document using JavaScript? I am not interested in images, digits, or tables - just the words so that I can manipulate them as JavaScript objects. ...

How can I download a PDF file in React.js using TypeScript on Next.js?

I've created a component to download a PDF file. The PDF file is named resumeroshan.pdf and it's located inside the Assets folder. "use client"; import resume from "/../../Assets/resumeroshan.pdf"; export default function Abo ...