Search code examples
c#asp.net-coreoauthdiscordauthorization

User.Identity.IsAuthenticated is false in a non-auth methods after successful login in asp.net core


I am new in asp.net core. I try to login with discord as 3rd party login service (like sign-in with facebook, google).

I can login successfully and have my user object, claims and I can enter a class which has an authorize attribute. Below you can see that UserIdentity is fine.

enter image description here

But let assume that user wants to go back to the login page. In this case, I have to redirect him to the index but I want to check whether the user is authenticated or not by using Identity and unfortunately, it is false and no claims etc. As I understand, it may be related with cookies or something similar. I also use different attribute for class (not authorize but AllowAnonymous) You can see below my Identity object

enter image description here

I am sharing my authentication code

services.AddAuthentication(options =>
{
    options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
           
})
  .AddCookie(options =>
   {
      options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
      options.Cookie.MaxAge = options.ExpireTimeSpan;
      options.SlidingExpiration = true;
      options.EventsType = typeof(CustomCookieAuthenticationEvents);
      options.AccessDeniedPath = "/auth/DiscordAuthFailed";

    })
     .AddJwtBearer(options =>
      {
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters()
        {
           ValidateIssuer = false,
           ValidateAudience = false,
           ValidateIssuerSigningKey = true,
           ValidIssuer = Configuration.GetValue<string>("Jwt:Issuer"),
           ValidAudience = Configuration.GetValue<string>("Jwt:Audience"),
           IssuerSigningKey = new SymmetricSecurityKey(
               Encoding.UTF8.GetBytes(Configuration.GetValue<string>("Jwt:EncryptionKey")))
        };
    })
     .AddOAuth("Discord",
         options =>
         {
           options.AuthorizationEndpoint = "https://discord.com/api/oauth2/authorize";
           options.TokenEndpoint = "https://discord.com/api/oauth2/token";
           options.Scope.Add("identify");
           options.Scope.Add("email");
           options.Scope.Add("guilds.join");
           options.Scope.Add("guilds.members.read");

          options.CallbackPath = "/auth/oauthCallback"; 
          options.ClientId = Configuration.GetValue<string>("Discord:ClientId");
          options.ClientSecret = Configuration.GetValue<string>("Discord:ClientSecret");
          options.UserInformationEndpoint = "https://discord.com/api/users/@me";
                                            
          options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
          options.ClaimActions.MapJsonKey(ClaimTypes.Name, "username");
          options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
          options.ClaimActions.MapJsonKey(ClaimTypes.IsPersistent, "verified"); 

          options.AccessDeniedPath = "/auth/DiscordAuthFailed";
          options.Events = new OAuthEvents()
           {                         
             OnCreatingTicket = async context =>
              {
               var request = new HttpRequestMessage(HttpMethod.Get,
               context.Options.UserInformationEndpoint);
               request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
               request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
                var response = await context.Backchannel.SendAsync(request,
                HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);

                response.EnsureSuccessStatusCode();

                var user=(await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync())).RootElement;

                context.RunClaimActions(user);

                }
          };
    });

services.AddTransient();

So my question is that, what is the best approach to access userIdentify object in any class/method after successfully login?


Solution

  • You can use the GetUserAsync method that will check if a user has been logged in. You need to use the UserManager class that falls under the AspNetCore.Identity to a use the above method. In your case, it will look something like this:

    You will first need to configure your UserManager class in Startup.cs by simply adding a parameter to the Configure method:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
     // your code
    }
    

    And then you can use it in your Controller method:

    [Route("Account")]
    public class AccountController: Controller
    {
    
       private UserManager<ApplicationUser> _userManager;
    
       public AccountController(UserManager<ApplicationUser> userManager)
       {
         _userManager = userManager;
       }
    
       [Route("Login")]
       [AllowAnonymous]
       public IActionResult Login()
       {
          ClaimsPrincipal currentUser = User;
          var user =_userManager.GetUserAsync(User).Result; 
          if(user.Identity.IsAuthenticated)
          {
            //redirect here
          }  
    
          return View();
       }
    }
    

    You need to update your ConfigureServices to include the Default Identity:

    public void ConfigureServices(IServiceCollection services)  
    {  
        services.AddDbContext<ApplicationDbContext>(options =>  
            options.UseMySql(  
                Configuration.GetConnectionString("DefaultConnection")));  
        services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)  
            .AddEntityFrameworkStores<ApplicationDbContext>();  
    }