"Secure access with Keycloak authentication in a .NET Core API and Vue.js Single Page Application

Currently, I am utilizing .NET Core for the backend REST API and Vue.js for the frontend.

In my current setup, I have implemented authentication through a cookie.

However, I am now looking to switch to authenticating via Keycloak. The goal is to have the authentication process go through Keycloak's authentication page instead of my Vue.js app's authentication page.

I have been unsuccessful in finding any sample code or resources to guide me on how to achieve this integration.

If I were to proceed with authentication using Vue.js, what steps should I take to ensure that the authentication is conducted on the .NET Core API? Should I be sending the token to the API or following another approach?

Answer №1

Here is the solution provided for your issue.

I made the necessary updates in the Startup.cs file as shown below:

private static bool ServerCertificateCustomValidation(HttpRequestMessage requestMessage, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslErrors)
    {
        //Inspecting the server-provided certificate
        Log($"Requested URI: {requestMessage.RequestUri}");
        Log($"Effective date: {certificate.GetEffectiveDateString()}");
        Log($"Expiration date: {certificate.GetExpirationDateString()}");
        Log($"Issuer: {certificate.Issuer}");
        Log($"Subject: {certificate.Subject}");

        //Deciding on certificate validity based on custom logic
        Log($"Errors: {sslErrors}");

        return true;
        //return sslErrors == SslPolicyErrors.None;
    }

    // Method to add services to the container
    public void ConfigureServices(IServiceCollection services)
    {
        
        services.AddAuthentication(options => {
            // Configuring authentication schemes
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(o =>
        {
            // Setting up JWT Bearer authentication options
            o.Authority = Configuration["Jwt:Authority"];
            o.Audience = Configuration["Jwt:Audience"];
            o.RequireHttpsMetadata = true;
            o.SaveToken = true;


            HttpClientHandler handler = new HttpClientHandler()
            {
                // Configurations for HttpClientHandler
                CheckCertificateRevocationList = false,
                UseDefaultCredentials = false,
                ClientCertificateOptions = ClientCertificateOption.Manual,
                SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11,
                ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => ServerCertificateCustomValidation(message, cert, chain, errors),
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
                CookieContainer = new CookieContainer()
            };
            
            handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
            o.BackchannelHttpHandler = handler;

            o.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "preferred_username"
            };

            o.Events = new JwtBearerEvents()
            {
                OnTokenValidated = c =>
                {
                    // Logic for token validation
                    var test = c;
                    
                    JwtSecurityToken accessToken = c.SecurityToken as JwtSecurityToken;
                    if (accessToken != null)
                    {
                        ClaimsIdentity identity = c.Principal.Identity as ClaimsIdentity;
                        if (identity != null)
                        {
                            identity.AddClaim(new Claim("access_token", accessToken.RawData));
                        }
                    }
                    return Task.CompletedTask;
                },
                OnAuthenticationFailed = c =>
                {
                    // Handling authentication failure
                    c.NoResult();
                    Log($"test");
                    c.Response.StatusCode = 500;
                    c.Response.ContentType = "text/plain";
                    return c.Response.WriteAsync("An error occurred processing your authentication." + c.Exception.InnerException.Message);
                }
            };
        });

        ...
        services.Configure<SecurityStampValidatorOptions>(options =>
        {
            options.ValidationInterval = TimeSpan.Zero;
        });

        services.AddControllers()
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.DateFormatString = "yyyy-MM-ddTHH:mm:ss";
            });
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp";
        });
    }

Furthermore, changes were made in the appsettings.json file:

"Jwt": {
"Authority": "https://auth.myauthority/auth/realms/myauthority",
"https": null,
"Audience": "MyAudience"

},

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

Should the HTTP Accept-Language header be utilized to determine the user's locale?

I am utilizing Vue.js in combination with Laravel. I am looking to set the user's local language. Is this method recommended? axios.interceptors.request.use( function (config) { config.headers['Accept-Language']='en' o ...

Tips on setting and utilizing custom environment variables in a Vue Electron application

What is the best way to integrate custom environment variables into my Electron application? I need to securely store API keys and other sensitive information without hardcoding them directly into the code. My app is built with Vue and Electron. To tackle ...

Unable to access component data while inside a v-for loop scope

I recently started using Vue and I'm having trouble accessing my component data within a v-for loop. After implementing the code below, I encountered this error message. TypeError: Cannot read property 'whatever' of undefined at eva ...

Binding Vue MultiSelect Checkboxes to Data Model

The data properties of the multi-select component are not updating when changed. The checkboxes are not being updated on the front-end. Expected Behavior: The checkboxes should get ticked when clicked. Link to code: https://jsfiddle.net/bzqd19nt/3/ < ...

Global Inertia Headers

How can I ensure that a custom header (Accept-Content-Language) is sent with every request, including Inertia manual visits? Below is the code snippet where I define and set the header: import axios from 'axios'; const lang = localStorage.getIt ...

Utilizing a JSON object to send data to a C# WebAPI POST endpoint that accepts a parameter of a generic abstract class

I have a generic abstract class that defines the structure of an object to be POSTed to this endpoint. The class is as follows, with an example implementation: public abstract class Animal<T> { public string Name { get; set; } pu ...

Error when trying to add style-loader in Vue.js 2 due to webpack installation issue

My task is to set up the style-loader in order to load import 'bootstrap-icons/font/bootstrap-icons.css'. However, when I run npm install style-loader, I encounter the following error message: npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to re ...

Having issues with v-for in Vuejs? It seems to be looping multiple times

<div v-for="item in items" :key="item.id"> <div v-for="accordion in accordions" :key="accordion.title" class="line" :class="{ green: accordion.text === 'AllaboutVue', red: accordi ...

Utilizing Vue and Typescript for efficient dependency injection

After attempting to use vue-injector, I encountered an issue as it was not compatible with my version of Vue (2.6.10) and Typescript (3.4.5). Exploring other alternatives, there seem to be limited options available. Within the realm of pure typescript, t ...

Connecting Vue components to CSS classes

I'm currently attempting to connect vue components to a specific class name so that it activates on every element with that class. However, I am running into an issue where it only works with the first element and not the others. <div class="__com ...

Enabling the value to be set as true for radio buttons generated using v-for

Utilizing a v-for loop over an array of objects in graphicState, the rows of a table are dynamically generated. My goal is to implement a column of radio buttons that, when checked, will set graphicState[index].selected to true. I came across this insight ...

How can a debounce function be customized to work on multiple instances of the same Vue.js component autonomously?

I am encountering an issue with two Vue components that each have a "save to disk" call triggered on every change of data. These components utilize a mixin to load the data, and they save into separate files in order to function independently (each trigger ...

Having trouble implementing an onClick event to change a button's CSS to href in Vue.js

When working with a SinglePageApp and using UIKit (UKit), I have found that I need to use a <button> tag instead of an <a> tag. Previously, the <a> tag was written like this: <a type="button" class="uk-button uk-button-link" href="#e ...

Updating the values of parent components in Vue.js 3 has been discovered to not function properly with composite API

Here is a Vue component I have created using PrimeVue: <template lang="pug"> Dialog(:visible="dShow" :modal="true" :draggable="false" header="My Dialog" :style="{ width: '50vw' }" ...

Adjust the scroll position when the height of a div is modified

Imagine we have a large div A with a height value and below it are other divs B, C, and more. If the user is viewing divs B or C, and A reduces its height by half, the scrolling position will remain the same. However, divs B and C will move up by that amo ...

Delaying the activation of the code until the image upload is complete

I'm having trouble synchronizing code to upload an image using a vue composable, wait for the upload to finish, and then store the Firebase storage URL into a database. Despite getting the URL, the success code fires before the upload is complete. My ...

Vue JS encounters difficulties when attempting to lazy load various images from a randomized image URL

I've implemented a code snippet for lazy loading images with different URLs: <img src="https://source.unsplash.com/1600x900/?hotel,booking.com?v=1" loading="lazy" /> <img src="https://source.unsplash.com/1600x900/?hot ...

Tips for displaying an associative object array as td elements within a tbody in Nuxt

I'm having trouble displaying the property of an associative object array in my code. I attempted to utilize a v-for loop and wanted to showcase the property information within the td elements of a tbody. I am aware that v-data-table components have a ...

Creating dynamic attribute names in Vue with Pug is a powerful combination that allows

Struggling to figure out the correct method for incorporating dynamic attribute names into a Vue template using Pug. Currently, I have the following setup: <template lang="pug"> button(id=`button__${buttontype}`) slot(name="text&qu ...

Get the application/pdf document that was just sent to you from the software backend

I am facing an issue with downloading a PDF file sent from the backend. Upon receiving a blob response, I notice that when I download and view the file, the sheets are empty, matching the expected number of sheets. Could this be a coding problem? Current ...