Authentication with Google OAuth 2.0

✅ Similar to Blazor Server

Using Google OAuth 2 for authentication can enhance the user experience and streamline the sign-up process, as many users already have a Google account and can log in to your application using their existing credentials. This tutorial builds on the basic JWT authentication by incorporating Google authentication through the use of Google Identity, allowing for both types of authentication to be used simultaneously. The tutorial covers the following steps:

  • Setting up a Google Cloud account
  • Setting up a Blazor project
  • Implementing login with Google using a custom button
  • Implementing login with Google using a rendered button
You can download the example code used in this topic on GitHub.

Setting up a Google Cloud account

You must first set up your Google Cloud account by following these steps:

  1. Go to the Google Cloud Dashboard.
  2. Near the Google Cloud logo at the top of the page, click Select a project.

crerate-google-cloud-project.png

  1. Choose New project.

crerate-google-cloud-project-2.png

  1. Enter your project name and location.

crerate-google-cloud-project-3.png

  1. Navigate to APIs & Services.

crerate-google-cloud-project-4.png

  1. Create a new OAuth consent screen and select External option.

crerate-google-cloud-project-5.png

  1. Fill in the required information and leave the rest of the steps as default.

crerate-google-cloud-project-6.png

  1. Once you have the OAuth consent screen, create a credential.

crerate-google-cloud-project-7.png

  1. Enter the name of the credential, and in the Authorized JavaScript origins field, add 2 URIs: http://localhost and http://localhost:<your_port>.

crerate-google-cloud-project-8.png

  1. After creating the credential, you will receive a client ID on the right side. You will need this client ID to connect to Google OAuth 2 later.

crerate-google-cloud-project-9.png


Setting up a Blazor project

It is recommended to review the Basic JWT Authentication tutorial and ensure that you have a functional basic JWT authentication system in place before proceeding with the following steps.

  1. Add a new JavaScript file to your wwwroot folder. For example:

BlazorSchoolGoogleOAuth.js:

let blazorSchoolAuthenticationStateProviderInstance = null;

function blazorSchoolGoogleInitialize(clientId, blazorSchoolAuthenticationStateProvider)
{
    // disable Exponential cool-down
    /*document.cookie = `g_state=;path=/;expires=Thu, 01 Jan 1970 00:00:01 GMT`;*/
    blazorSchoolAuthenticationStateProviderInstance = blazorSchoolAuthenticationStateProvider;
    google.accounts.id.initialize({ client_id: clientId, callback: blazorSchoolCallback });
}

function blazorSchoolGooglePrompt()
{
    google.accounts.id.prompt((notification) =>
    {
        if (notification.isNotDisplayed() || notification.isSkippedMoment())
        {
            console.info(notification.getNotDisplayedReason());
            console.info(notification.getSkippedReason());
        }
    });
}

function blazorSchoolCallback(googleResponse)
{
    blazorSchoolAuthenticationStateProviderInstance.invokeMethodAsync("GoogleLogin", { ClientId: googleResponse.clientId, SelectedBy: googleResponse.select_by, Credential: googleResponse.credential });   
}
  1. Import the Google GSI Client JavaScript and your JavaScript file into index.html.
<script src="_framework/blazor.webassembly.js"></script>
<script src="js/BlazorSchoolGoogleOAuth.js"></script>
<script src="https://accounts.google.com/gsi/client" async defer></script>
  1. Create a class that represents the response from Google. For example:
public class GoogleResponse
{
    public string ClientId { get; set; } = "";
    public string SelectedBy { get; set; } = "";
    public string Credential { get; set; } = "";
}
  1. Add a new method to your User class to create a new user from the Google JWT. For example:
public class User
{
    ...
    public static User? FromGoogleJwt(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();

        if (tokenHandler.CanReadToken(token))
        {
            var jwtSecurityToken = tokenHandler.ReadJwtToken(token);

            return new()
            {
                Username = jwtSecurityToken.Claims.First(c => c.Type == "name").Value,
                Password = ""
            };
        }

        return null;
    }
}

For more information about Google JWT, see https://developers.google.com/identity/gsi/web/reference/js-reference#credential.

  1. In your AuthenticationStateProvider, add a method for Google login that can be invoked by JavaScript. For example:
public class BlazorSchoolAuthenticationStateProvider : AuthenticationStateProvider, IDisposable
{
    ...
    [JSInvokable]
    public void GoogleLogin(GoogleResponse googleResponse)
    {
        var principal = new ClaimsPrincipal();
        var user = User.FromGoogleJwt(googleResponse.Credential);
        CurrentUser = user;

        if (user is not null)
        {
            principal = user.ToClaimsPrincipal();
        }

        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
    }
}

Implementing login with Google using a custom button

A custom button provides more design options, allowing you to create a button that matches your website's style. For instance:

@inject IJSRuntime JSRuntime
@inject BlazorSchoolAuthenticationStateProvider BlazorSchoolAuthenticationStateProvider

<button type="button" @onclick="SignInWithGoogleAsync">Sign In with Google</button>

@code {
    public async Task SignInWithGoogleAsync()
    {
        var blazorSchoolAuthenticationStateProvider = DotNetObjectReference.Create<BlazorSchoolAuthenticationStateProvider>(BlazorSchoolAuthenticationStateProvider);
        await JSRuntime.InvokeVoidAsync("blazorSchoolGoogleInitialize", "686658243135-3s5lnkgih8so1ckhbhrntuilp2vrf161.apps.googleusercontent.com", blazorSchoolAuthenticationStateProvider);
        //The following code might not work in some cases
        //await JSRuntime.InvokeVoidAsync("google.accounts.id.prompt");
        
        //The following code will execute the prompt function and print the reason of not working to the console if fails.
        await JSRuntime.InvokeVoidAsync("blazorSchoolGooglePrompt");
    }
}

The <button> element with @onclick="SignInWithGoogleAsync" is the custom Sign In with Google button that the user will click to initiate the sign-in process.

In the SignInWithGoogleAsync method, the blazorSchoolGoogleInitialize function is invoked with the client ID and authentication state provider as parameters. Then, the blazorSchoolGooglePrompt function is used to trigger the sign-in process.


Implementing login with Google using a rendered button

Using the default button provided by Google's sign-in API is a quick and easy solution for adding Google sign-in functionality to your website. While it may enhance the user experience and provide a more professional look to your website, it does have limitations in terms of customization. For instance:

@inject IJSRuntime JSRuntime
@inject BlazorSchoolAuthenticationStateProvider BlazorSchoolAuthenticationStateProvider

<div id="blazor-school-button"></div>

@code {
    protected override async Task OnInitializedAsync()
    {
        var blazorSchoolAuthenticationStateProvider = DotNetObjectReference.Create<BlazorSchoolAuthenticationStateProvider>(BlazorSchoolAuthenticationStateProvider);
        await JSRuntime.InvokeVoidAsync("blazorSchoolGoogleInitialize", "686658243135-3s5lnkgih8so1ckhbhrntuilp2vrf161.apps.googleusercontent.com", blazorSchoolAuthenticationStateProvider);
        var element = await JSRuntime.InvokeAsync<IJSObjectReference>("document.getElementById", "blazor-school-button");
        await JSRuntime.InvokeVoidAsync("google.accounts.id.renderButton", element, new { theme = "filled_blue", size = "large" });
    }
}

The <div> element with id="blazor-school-button" is where the Google sign-in button will be rendered. In the OnInitializedAsync method, the blazorSchoolGoogleInitialize function is invoked with the client ID and authentication state provider as parameters. Then, the getElementById function is used to get a reference to the div element where the Google button will be rendered. Finally, the renderButton function is used to render the Google sign-in button with the specified theme and size.

For more information about rendered button, see https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.renderButton.

BLAZOR SCHOOL
Designed and built with care by our dedicated team, with contributions from a supportive community. We strive to provide the best learning experience for our users.
Docs licensed CC-BY-SA-4.0
Copyright © 2021-2024 Blazor School
An unhandled error has occurred. Reload 🗙