Search code examples
c#owinidentityserver4bearer-tokenowin-middleware

IdentityServer, ASP.NET Core and Web API


I have setup the IdentityServer4 and another client ASP.NET Core application. The Client needs to authenticate with the IdentityServer and request access to a third application which is standard MVC Web API project.

i have followed the steps to achieve Client Credentials Flow from this example https://identityserver.github.io/Documentation/docsv2/overview/simplestOAuth.html

Now I am completely lost on how to get the WEB API to first recognize the Bearer tokens and then give me some Authorization to access the web api endpoints.

this is my IdentityServer Statrup.cs

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddIdentityServer()
                .AddInMemoryStores()
                .AddInMemoryClients(Config.GetClients())
                .AddInMemoryScopes(Config.GetScopes());           
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(LogLevel.Debug);
        app.UseDeveloperExceptionPage();

        app.UseIdentityServer();
    }
}

and the Config.cs

 public class Config
{
    // scopes define the resources in your system
    public static IEnumerable<Scope> GetScopes()
    {
        return new List<Scope>
        {
            new Scope
            {
                Name = "api1"
            }
        };
    }

    // clients want to access resources (aka scopes)
    public static IEnumerable<Client> GetClients()
    {
        // client credentials client
        return new List<Client>
        {
            // no human involved
        new Client
        {
            ClientName = "Silicon-only Client",
            ClientId = "silicon",
            Enabled = true,
            AccessTokenType = AccessTokenType.Reference,

            AllowedGrantTypes = GrantTypes.ClientCredentials,

            ClientSecrets = new List<Secret>
            {
                new Secret("F621F470-9731-4A25-80EF-67A6F7C5F4B8".Sha256())
            },

            AllowedScopes = new List<string>
            {
                "api1"
            }
        }
        };
    }}

ASP.NET Core calls to the IdentityServer and Web API

[Route("api/testing")]
    public class TestingController : Controller
    {
        // GET: api/values

        [HttpGet]
        public IActionResult Get()
        {
            var responce = GetClientToken();

            return Json(new
            {
                message = CallApi(responce)
            });
        }

        static TokenResponse GetClientToken()
        {
            var client = new TokenClient(
                Constants.TokenEndpoint,
                "silicon",
                "F621F470-9731-4A25-80EF-67A6F7C5F4B8");

            return client.RequestClientCredentialsAsync("api1").Result;
        }

        static string CallApi(TokenResponse response)
        {
            var client = new HttpClient
            {
                BaseAddress = new Uri(Constants.AspNetWebApiSampleApi),
                Timeout = TimeSpan.FromSeconds(10)
            };
            client.SetBearerToken(response.AccessToken); 
            try
            {                  
                var auth = client.GetStringAsync().Result;
                return auth;
            }
            catch (Exception ex)
            {
                return ex.Message;

            }
        }  
    }

So can anyone explain or share some links on what should i do to get the WEB APi (with owin middleware) to handle the calls from the ASP.NET Core client? What setting should i put in the Owin Configuration(IAppBuilder app) method.


Solution

  • First of all, you have to add a ScopeType to your api-scope, as your API is handling Resources you need to add ScopeType.Resource if you want to show a consent screen at your MVC-Client you should also add a display name and if you need further claims at your api, add them with a list of claims:

    new Scope
    {
        Name = "api",
        DisplayName = "Your scopes display name",
        Type = ScopeType.Resource,
        Claims = new List<ScopeClaim>
        {
            new ScopeClaim("role")
        }
    }
    

    At your api-project you also have to add a OwinStartup class because thats where you tell your api to use bearer token authorization: In the Configuration of Startup:

    app.UseIdentityServerBearerTokenAuthentication(
        new IdentityServerBearerTokenAuthenticationOptions
        {
            Authority = "YourIdentityServersAddress.com/identity",
            RequiredScopes = new[] { "api" },
         });
     app.UseWebApi(WebApiConfig.Register());
    

    The last line is just to register your webapi-config. Thats where you specify your Authority i.e.(that means) your Identity-Server and the scope you specified in the IdentityServers Startup-File. If you also want to add a Custom AuthorizationManager (e.g. for authorizing for specific roles you need to add the following line before the "UseIdentityServerBearer..." in the Startup of your API:

    app.UseResourceAuthorization(new AuthorizationManager());
    

    Where the AuthorizationManager is a class which you implement by your own by inheriting from the class ResourceAuthorizationManager. But I don't want to get to deep on this(if you have further questions about this I'll be happy to go deeper)

    And at your API's controller you just have to add the [Authorize] Attribute above the methods you wan't to have authorized access at or at the class level if your whole controller should be required to be authorized. (If you want to use e.g. the roles authentication you would need the [ResourceAuthorize].

    If you have further questions or want to know feel free to ask