ClubberLang
ClubberLang

Reputation: 1898

Protect ASP.NET API with IdentityServer & Bearer

I have a 3 tier application, where I have:

Currently, I have 2 kinds of controllers on the HTTP API:

Right now, the "personal purpose API" working fine. this API is only accessible when the user is logged in.

But, I also need to protect the "general-purpose API" from any hacker, right now a call to "post/list" returns an "unauthorized error"!

I wish to protect this API, without authentication, but it must be only accessible from my web application.

Do you know how can I do this? Is there something wrong or missing in my code?

Here is the controller code on the HTTP API side :

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class PostsController : ControllerBase
{
        [HttpGet]
        [Route("post/list")]
        public async Task<List<Item>> GetListAsync()
        {
            ...
        }
}

Here is my code on the HTTP API side :

context.Services.AddAuthentication(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
  options.Authority = configuration["AuthServer:Authority"];
  options.RequireHttpsMetadata = true;
  options.ApiName = "SoCloze";
});

And here is my code on the Web Application side:

context.Services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies", options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromDays(ApplicationConstants.LoginCookieExpirationDelay);
        })
        .AddOpenIdConnect("oidc", options =>
        {
            options.Authority = configuration["AuthServer:Authority"];
            options.RequireHttpsMetadata = true;
            options.ResponseType = OpenIdConnectResponseType.CodeIdToken;

            options.ClientId = configuration["AuthServer:ClientId"];
            options.ClientSecret = configuration["AuthServer:ClientSecret"];

            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;

            options.Scope.Add("role");
            options.Scope.Add("email");
            options.Scope.Add("phone");
            options.Scope.Add("SoCloze");

            options.ClaimActions.MapAbpClaimTypes();
        });

Upvotes: 0

Views: 362

Answers (2)

Tore Nestenius
Tore Nestenius

Reputation: 19921

To keep the API simple, you of course want to only accept tokens from the trusted IdentityServer.

So basically you need a generic access token that is secure, but not tied to any human user. You could have the web app to request an access token using the client credentials flow and then use that token all the time when you want to access the API as a non-authenticated user.

In IdentityServer you would setup a separate client just for this.

Also using the IdentityModel library could help you out here (as a Worker Applications) https://identitymodel.readthedocs.io/en/latest/aspnetcore/worker.html

see also https://docs.duendesoftware.com/identityserver/v5/tokens/requesting/

This picture shows what I mean:

enter image description here

Upvotes: 1

Alpha75
Alpha75

Reputation: 2280

You should make your controller anonymous. With your configuration simply remove your Authorize attribute from your controller. If you had applied a global policy you'd need to add the anonymous attribute to your controller:

[AllowAnonymous]

I'm not sure what you want to achieve, but I guess you want cross-site protection. That is, you want to limit certain endpoints from being accessed only from the origin of your blazor app.

You need to trust in the client browser for Same Origin Policy. The same origin policy controls interactions between two different origins. Cross-origin writes are typically allowed, Cross-origin embedding is typically allowed and Cross-origin reads are typically disallowed, but read access is often leaked by embedding.

This protection is only for XHR calls from your browser and it doesn't protect againts direct access (postman, fiddler, any http client...).

To prevent cross-origin writes in your posts you can use Antiforgery tokens. Asp Net Core create this tokens automatically if you specify POST in your forms:

<form asp-controller="Todo" asp-action="Create" method="post">
    ...
</form>

Or

@using (Html.BeginForm("Create", "Todo"))
{
    ...
}

This will reject any post from any form that has not been previously loaded from your server.

Upvotes: 1

Related Questions