Contex.User.IsAuthenticated = false in next request after successfully OAuth2 SSO login from custom provider

51 Views Asked by At

I'm setting up a .Net 8 asp mvc app with and external Oauth2 SSO Login. So I using a custom AuthenticationHandler with these HandleAuthenticateAsync() & HandleChallengeAsync()

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
    try
    {
        AuthenticationProperties properties = null;

        string code = "";
        string state = "";
        var values = Request.Query["code"];
        if (values.Count == 1)
            code = values[0];

        values = Request.Query["state"];
        if (values.Count == 1)
            state = values[0];

        if (!string.IsNullOrEmpty(state))
            properties = Options.StateDataFormat.Unprotect(state);

        if (properties == default(AuthenticationProperties))
        {
            if (!Options.CallbackPath.HasValue || Options.CallbackPath != Request.Path)
                return AuthenticateResult.Fail("Invalid state parameter");
        }
        
        
        
        /*
        
        Here I ask for an accessToken and with it I request the user information from the auth server
        And do logic about the roles and claims

        await _signInManager.SignInAsync(applicationUser, isPersistent: true, "myScheme");
        //Success! Add details here that identifies the user

        var newClaims = await _userManager.GetClaimsAsync(applicationUser);
        newClaims.Add(new Claim("Name", user.given_name));
        
        var userRoles = await _userManager.GetRolesAsync(applicationUser);
        foreach (var role in userRoles)
        {
            newClaims.Add(new Claim(ClaimTypes.Role, role));

        }
        var Identity = new ClaimsIdentity(newClaims, "id_code", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
        var claimsPrincipal = new ClaimsPrincipal(Identity);

        await Options.Provider.Authenticated(Context);
        return AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, properties, "myScheme"));
    }
    catch (Exception ex)
    {
        return AuthenticateResult.Fail("Unauthorized");
    }
}


protected override async Task<Task> HandleChallengeAsync(AuthenticationProperties properties)
{
    try
    {
        var baseUri =
            Request.Scheme +
            Uri.SchemeDelimiter +
            Request.Host +
            Request.PathBase;

        var currentUri =
            baseUri +
            Request.Path +
            Request.QueryString;

        var redirectUri =
            baseUri +
            Options.CallbackPath;

        //var properties = challenge.Properties;
        if (string.IsNullOrEmpty(properties.RedirectUri))
        {
            properties.RedirectUri = currentUri;
        }

        // Generate a correlation ID and store it in AuthenticationProperties
        var correlationId = Guid.NewGuid().ToString();
        properties.Items[".AspNet.Correlation.id_code"] = correlationId;

        // comma separated
        var scope = string.Join(",", Options.Scope);

        var state = Options.StateDataFormat.Protect(properties);

        var optionsScope = Options.GetScope();
        var scopeToUri = Uri.EscapeDataString(optionsScope);

        var authorizationEndpoint =
            Options.Endpoints.AuthorizationEndpoint +
            "&client_id=" + Uri.EscapeDataString(Options.ClientId) +
            "&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
            "&scope=" + scopeToUri +
            "&response_type=" + "code" +
            "&state=" + Uri.EscapeDataString(state);

        Response.Redirect(authorizationEndpoint);

        return Task.CompletedTask;
    }
    catch (Exception e)
    {
        _logger.LogError(e.Message);
        throw new Exception(e.Message);
    }
}

public static class myAuthenticationExtensions
{
    public static AuthenticationBuilder UseMyAuthentication(
        this AuthenticationBuilder builder,
        string authenticationScheme,
        Action<myAuthenticationOptions> configureOptions)
    {
        if (configureOptions == null)
            throw new ArgumentNullException(nameof(configureOptions));
        
        return builder.AddScheme<myAuthenticationOptions, myAuthenticationHandler>(
            authenticationScheme, configureOptions);
    }
}

and here my are my ConfigureServices() and Configure()

public void ConfigureServices(IServiceCollection services)
{
    services.adddbcontext<applicationdbcontext>(options =>
      options.usesqlserver(configuration.getconnectionstring("myConnection")));

    services.addidentity<applicationuser, applicationrole>()
        .addentityframeworkstores<applicationdbcontext>()
        .adddefaulttokenproviders()
    .addsigninmanager<signinmanager<applicationuser>>()
    .addusermanager<usermanager<applicationuser>>()
    .addrolemanager<rolemanager<applicationrole>>();
    //services.addscoped<rolemanager<applicationrole>>();
    //services.addscoped<usermanager<applicationuser>>(); // assuming applicationusermanager is a non-generic type
    //services.addscoped<signinmanager<applicationuser>>(); // assuming applicationsigninmanager is a non-generic type

    // add authentication services
    services.addauthentication(options =>
    {
        options.defaultauthenticatescheme = "myScheme";
        options.defaultchallengescheme = "myScheme"; // your custom authentication scheme
    })
    .UseMyAuthentication("myScheme", options =>
    {
        // set options properties using values from appsettings.json
        options.clientid = configuration.getsection("appsettings")["authserver:clientid"];
        options.clientsecret = configuration.getsection("appsettings")["authserver:clientsecret"];
        options.callbackpath = new pathstring("/oauth/callback");

        var dataprotectionprovider = services.buildserviceprovider().getdataprotectionprovider();
        var dataprotector = dataprotectionprovider.createprotector(typeof(uscauthenticationextensions).fullname);
        options.statedataformat = new propertiesdataformat(dataprotector);

        options.provider = new myAuthenticationprovider();

        options.endpoints.authorizationendpoint = configuration.getsection("appsettings")["authserver:authorizationendpoint"];
        options.endpoints.tokenendpoint = configuration.getsection("appsettings")["authserver:tokenendpoint"];
        options.endpoints.userinfoendpoint = configuration.getsection("appsettings")["authserver:userinfoendpoint"];
    })
    .addcookie();


    // add services to the container.
    services.addrazorpages();
    services.addhttpforwarder();
    services.addhttpclient();
    services.addhttpcontextaccessor();
}


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    // Use the routes configured in RouteConfig
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapRazorPages();
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

Well, when I run the app by first time, it enters the HandleAuthenticateAsync () and retrieve a Fail after to check that properties == null Then enter the HandleChallengeAsync () which shows me up the provider login, after to successfully enter my credentials, the app starts to load the _loginPartial and debugging the razor pages I see that the IsAuthenticated = true and the users, roles and claims. But the problems are when it comes to a second request, which is while loading the rest of the page, it enters HandleAuthenticateAsync () with
signInManager.Context.IsAuthenticated = false I'm expecting to have the user already logged in, the signInManager the App. And worst, the HandleChallengeAsync () is not called anymore

I don't know what I'm loosing in the auth scheme.

Can anybody help with this?

I'm expecting to login onece and the navigate across the application with the user credentials.

0

There are 0 best solutions below