Skip to content

Shaddix/IdentityOAuthSpaExtensions

Repository files navigation

IdentityOAuthSpaExtensions

.NET 6.0 library (maintained since 2019) that allows easy integration of external OAuth providers (Google, Facebook, etc.) into your SPA. It is most useful together with IdentityServer.

With IdentityOAuthSpaExtensions you can integrate Login with Facebook or Login with Google buttons right inside your SPA (React/Angular/whatever) without the need to redirect to server-side UI for authentication.

Here's the demo if you care. The shown page uses vanilla JS, and has several buttons to log in via different providers. Example workflow

What you can do with this library?

  • You could add Social Log In buttons inside your SPA (so you don't need to show default Identity UI to your users)
  • On SPA side you could receive AuthCode from OAuth provider (Authorization Code Flow)
  • On backend you could verify AuthCode (passed from your SPA) and get user information from oAuth provider
  • If you're using IdentityServer, you could plug-in an extension grant that will allow you to issue your own JWT tokens in exchange for AuthCode (and optionally create new users).

Goal

The project goal is to allow integration of external OAuth providers (e.g. Google, Facebook, etc.) into your SinglePageApplications applications (React, Angular, plain-old-js, whatever), with minimum amount of needed code, and without the need to show Identity UI to the user.

This is a backend library, that integrates with Asp.Net Core 5.0+.

The library is kept minimal, as we reuse all official and non-official authentication providers (i.e. library doesn't need to be updated when those external providers change).

Library is significantly reworked in v1.0, so there's no provider-specific code at all.

How to

Backend

  1. Install nuget to add the library to your project.

    dotnet add package IdentityOAuthSpaExtensions

  2. From ConfigureServices call services.ConfigureExternalAuth().

  3. From Configure call app.UseExternalAuth() BEFORE UseAuthentication().

  4. If you are using IdentityServer, add the grant validator: services.AddIdentityServer().AddExtensionGrantValidator<ExternalAuthenticationGrantValidator<IdentityUser, string>>().

    Also include 'external' grant type to existing grant types of your Client (SPA)

  5. That's it. Just .AddAuthentication().AddGoogle() or .AddFacebook() as usual. Follow instructions on how to set up applications on OAuth provider side.

You could also take a look at IdentityOAuthSpaExtensions.Example for example usage (keep in mind, that there are hardcoded ClientId/ClientSecret for FB and Google within Example app. They are for demo purposes and everyone can use them, so beware).

Frontend

  1. Copy the following typescript (or compiled JS) file into your sourcecode (adjust the backendUri variable on top if your SPA is on different host than backend).
  2. Place some SocialLogin buttons in your SPA and execute getOAuthCode('Google') from onClick handler.
  3. Request access token from your Identity Server, passing received oAuthCode to it. In total you should have something like this:
    // call this function from SocialLogin buttons onClick  
    async function signInVia(provider) {
        const data = await getOAuthCode(provider);
        await getAccessToken(data.provider, data.code);
    }
    
    async function getAccessToken(provider, code) {
        const response = await fetch('/connect/token',
                {
                    method: 'POST',
                    // you will need to adjust SCOPE here
                    body: grant_type=external&scope=local&provider=${provider}&code=${code}`,
                    headers: {
                        // you definitely need to use clien/secret of YOUR APP here
                        'Authorization': 'Basic Y2xpZW50OnNlY3JldA==', //base64 encoded 'client:secret'
                        'Content-Type': 'application/x-www-form-urlencoded',
                    }
                });
        const jsonData = await response.json();
        _accessToken = jsonData.access_token;
        alert('access_token: '+ _accessToken);
        // make some requests to your API using this token!
    }    
    

Identity Server integration

Adding external grant (validate Auth Code and issue own JWT)

Typical scenario is that you use oAuth for authentication only, and then create the user in your local DB (via e.g. IdentityServer) and issue your own JWT with custom claims for later authorization. This library perfectly supports this scenario in combination with IdentityServer using extension grants (https://docs.identityserver.io/en/latest/topics/grant_types.html#extension-grants). To integrate with IdentityServer all you need to do is call services.AddIdentityServer().AddExtensionGrantValidator<ExternalAuthenticationGrantValidator<IdentityUser, string>>(). That will register an extension grant named external and you could authenticate from JS as described above

Customization

You could inherit from ExternalAuthenticationGrantValidator<IdentityUser, string> and provide your custom logic for any of the following methods:

  • CreateNewUser - fill-in the fields of new User based on your business requirements and/or information received from oAuth provider
  • CreateResultForLocallyNotFoundUser - here you could write your own business logic, regarding what to do when the user is logging in for the first time. You could write custom logic for user creation, or deny some users (based on email/id) from logging in.
  • GetUserName - most useful if you don't override CreateNewUser. You could provide Username for newly created users based on oAuth provider info

External user storage

We use standard Asp.Net Identity mechanism to store external logins (namely, AspNetUserLogins table). To find a user by external OAuth id you need to use _userManager.FindByLoginAsync(providerName, externalUserId)

About

.NET 5 library that allows integration of external OAuth providers right into your SPA UI (without Identity UI)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published