skip to Main Content

I have a .Net core 3.2 site with a RESTful API on one server, and a client website on another server. Users authenticate to the client app via only external providers such as Facebook, Google, or Microsoft. We also have an Identity Server 4.0 that we will be using, but will act just like another external provider.

The issue is that once the user is authenticated on the client, and their granted roles/claims have been determined, how do we request a particular resource from the API? The client web app knows about the user and knows the user is who they say they are, and the client app knows what they can do. How do we relay that information securely to the API?

I was considering client_credentials between the API and the web site, but it seems that is for situations where there is no user, like services or daemons.

I don’t want the API to know or care about the users, just that they are authenticated and what their claims are.

2

Answers


  1. Chosen as BEST ANSWER

    The answer I was looking for was JWT Tokens: On the client, before it sends the bearer token:

     protected override async Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request,
                CancellationToken cancellationToken)
            {
                var accessToken = await GetAccessTokenAsync();
    
                if (!string.IsNullOrWhiteSpace(accessToken))
                {
                    request.SetBearerToken(accessToken);
                }
    
                return await base.SendAsync(request, cancellationToken);
            }
    
      public async Task<string> GetAccessTokenAsync()
            {
    
                var longKey = "FA485BA5-76C3-4FF5-8A33-E3693CA97002";
    
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(longKey));
                var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
    
                var claims = new List<Claim> {
                    new Claim("sub",  _httpContextAccessor.HttpContext.User.GetUserId())
                };
    
                claims.AddRange(_httpContextAccessor.HttpContext.User.Claims);
                
                var token =new JwtSecurityToken(
                    issuer: "https://localhost:44389",
                    audience: "https://localhost:44366",
                    claims: claims.ToArray(),
                    expires: DateTime.Now.AddMinutes(30),
                    signingCredentials: credentials
                );
    
                return new JwtSecurityTokenHandler().WriteToken(token);
            }
    

    And on the API server

     var longKey = "FA485BA5-76C3-4FF5-8A33-E3693CA97002";
    
                services.AddAuthentication(x=> {
                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                    .AddJwtBearer(options =>
                    {
                        options.SaveToken = true;
                        options.RequireHttpsMetadata = false;
                        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                        {
                            ValidateIssuer = false,
                            ValidateAudience = false,
                            //ValidateLifetime = true,
                            ValidateIssuerSigningKey = true,
                            //ValidIssuer = "https://localhost:44366",
                            //ValidAudience = "https://localhost:44366",
                            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(longKey)),
                            //ClockSkew = TimeSpan.Zero
                        };
    
                        
                    });
    

  2. To implement authentication in a single-page application, you need to use Authorization Code with PKCE OAuth2 flow. It lets you not store any secrets in your SPA.

    Please don’t use Implicit flow as it’s deprecated because of security reasons.

    When you send your token from a client to a properly configured .NET Core API, you should be able to read the User property of the controller for the identity information.

    If you configure the API properly, a request will reach a controller only in case if an access token is valid.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search