0

I have a Net 6 backend app, and I am trying to add external auth using Microsoft, from React SPA app, so in the backend, I have these functions:

  [HttpPost]
    [AllowAnonymous]
    public IActionResult ExternalLogin([FromBody] ExternalLoginModel model)
    {
        // Request a redirect to the external login provider.
        var redirectUrl = $"https://localhost:44366/api/Accounts/ExternalLoginCallback?returnUrl={model.ReturnUrl}";
        var properties = _signInManager.ConfigureExternalAuthenticationProperties(model.Provider, redirectUrl);
        properties.AllowRefresh = true;
        return Challenge(properties, model.Provider);
    }

    [HttpGet("ExternalLoginCallback")]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        if (remoteError != null)
        {
            // Handle external authentication error
            return BadRequest($"Error from external provider: {remoteError}");
        }

        // Get the login information about the user from the external login provider
        var info = await _signInManager.GetExternalLoginInfoAsync();
        if (info == null)
        {
            // If the user does not have an account, ask the user to create an account.
            return BadRequest("External login information not found.");
        }

        // Sign in the user with this external login provider if the user already has a login.
        var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
        if (result.Succeeded)
        {
            // If successful, redirect to the returnUrl if provided or a default page.
            return Redirect(returnUrl ?? "/");
        }
        return null;
        
    }

and this is my program.cs configurations :

builder.Services.AddIdentityCore<User>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<DbCon>()
.AddSignInManager()
.AddTokenProvider(TokenOptions.DefaultProvider, typeof(DataProtectorTokenProvider<User>))
.AddTokenProvider(TokenOptions.DefaultEmailProvider, typeof(EmailTokenProvider<User>))
.AddTokenProvider(TokenOptions.DefaultPhoneProvider, typeof(PhoneNumberTokenProvider<User>))
.AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, typeof(AuthenticatorTokenProvider<User>));

builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.Cookie.Name = IdentityConstants.ApplicationScheme;
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/AccessDenied";
options.SlidingExpiration = true;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = true;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidIssuer = builder.Configuration["JWTSettings:Issuer"],
    ValidateIssuerSigningKey = true,
    IssuerSigningKey = new SymmetricSecurityKey(
        Encoding.ASCII.GetBytes(builder.Configuration["JWTSettings:Key"]
    )),
    ValidAudience = builder.Configuration["JWTSettings:Audience"],
    ValidateAudience = true,
    ValidateLifetime = true,
    ClockSkew = TimeSpan.FromMinutes(1)
};
x.Events = new JwtBearerEvents
{
    OnAuthenticationFailed = context =>
    {
        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
        {
            context.Response.Headers.Add("Token-Expired", "true");
        }
        return Task.CompletedTask;
    }
};
})
.AddMicrosoftAccount(microsoftOptions =>
{
microsoftOptions.ClientId = builder.Configuration["AzureAd:ClientId"];
microsoftOptions.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
microsoftOptions.SignInScheme = 
Microsoft.AspNetCore.Identity.IdentityConstants.ExternalScheme;
});

 // Cookie Policy needed for External Auth
builder.Services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a 
given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
});

the page Microsoft login appears and I fill in the email and password after that I get this error,

"error": "An error was encountered while handling the remote login."

and in debug I get this error in my inner error message "Correlation failed" , I hope you could help me with this

2

1 Answer 1

0
  • You can use the fetch API or any HTTP library to send a request to the ExternalLogin endpoint.

MicrosoftLogin.js :

import React, { useEffect } from 'react';
import { useHistory } from 'react-router-dom';

const MicrosoftLogin = () => {
  const history = useHistory();

  const handleMicrosoftLogin = () => {
    // Replace with your actual backend API URL
    const backendUrl = 'https://localhost:44366/api/Accounts/ExternalLogin';

    // Redirect the user to the backend Microsoft login endpoint
    window.location.href = `${backendUrl}?provider=Microsoft&returnUrl=${encodeURIComponent('/')}`;
  };

  useEffect(() => {
    // Check if the current URL has information about the external login
    const params = new URLSearchParams(window.location.search);
    const provider = params.get('provider');

    if (provider === 'Microsoft') {
      // Clear the URL parameters
      window.history.replaceState({}, document.title, '/');

      // You may want to trigger further actions, e.g., fetch user details or update UI
      console.log('Logged in with Microsoft');

      // Redirect to the desired page after successful login
      history.push('/');
    }
  }, [history]);

  return (
    <div>
      <button onClick={handleMicrosoftLogin}>Login with Microsoft</button>
    </div>
  );
};

export default MicrosoftLogin;

App.js :

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import MicrosoftLogin from './MicrosoftLogin';

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/microsoft-login" component={MicrosoftLogin} />
        {/* Add other routes as needed */}
      </Switch>
    </Router>
  );
};

export default App;
  • Visit http://localhost:3000 and click the "Login with Microsoft" button. The user will be redirected to your ASP.NET Core backend for Microsoft authentication, and upon successful login, they will be redirected back to the root page (/)

enter image description here

Addcookie configuration:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    options.Cookie.Name = IdentityConstants.ApplicationScheme;
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
    options.LoginPath = "/Account/Login";
    options.AccessDeniedPath = "/Account/AccessDenied";
    options.SlidingExpiration = true;
});
Sign up to request clarification or add additional context in comments.

2 Comments

well, I did the same, however, the problem is not on the front end, the problem is on the backend I think there is something i should add to my program.cs , I tried all solutions and I added the code for my backend, but still, i get logging and I get the Correlation failed error
Set the SameSite property for the authentication cookies to SameSiteMode.None if your application is making cross-origin requests.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.