0
votes

I am using Identity Server 4 with the Ws-Federation plugin. Identity Server is configured to connect to Azure AD for authentication. Here is the relevant code from Identity Server project:

public void ConfigureServices(IServiceCollection services)
        {
            var rsaCertificate = new X509Certificate2("rsaCert.pfx", "1234");

            services.AddRazorPages();
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<User, IdentityRole>(options =>
            {
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(2);
                options.Lockout.MaxFailedAccessAttempts = 3;
            })
            .AddDefaultUI()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddUserStore<CustomUserStore>()
            .AddUserManager<CustomUserManager>()
            .AddDefaultTokenProviders();

            services.AddTransient<IUserStore<User>, CustomUserStore>();
            services.AddTransient<IEmailSender, EmailSender>();

            var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;

                // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
                options.EmitStaticAudienceClaim = true;
            })
            .AddSigningCredential(rsaCertificate)
            .AddInMemoryIdentityResources(IdentityConfig.IdentityResources)
            .AddInMemoryApiScopes(IdentityConfig.ApiScopes)
            .AddInMemoryClients(IdentityConfig.Clients)
            .AddAspNetIdentity<User>()
            .AddWsFederationPlugin(options =>
            {
                options.Licensee = "Licensee";
                options.LicenseKey = "LicenseKey";
            })
            .AddInMemoryRelyingParties(new List<RelyingParty>());

            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
            })
            .AddWsFederation(options =>
            {
                options.Wtrealm = "Azure AD App Id";
                options.MetadataAddress = "WSFed metadata URL from Azure AD App";
                options.Events.OnSecurityTokenValidated = SecurityTokenValidated;
            })
            .AddCookie(options =>
            {
                options.ExpireTimeSpan = TimeSpan.FromMinutes(IdentityConfig.SessionTimeoutInMinutes);
                options.SlidingExpiration = true;
                options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
            });
        }

I have an API protected with JWT bearer authentication which is connected to the same Identity Server. The relevant code from the API (Please note that https://localhost:5001 is the address which the Identity Server is running in):

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                options.Authority = "https://localhost:5001";

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false
                };
            });
        }

I have an MVC client as well which is connected to the same Identity Server. I was able to successfully authenticate users from the MVC client. Now, what I would like to do is to call a protected API endpoint in the API project from within the MVC client. I haven't found any way to get the access token necessary for calling the protected API. Relevant code from the MVC client:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
            })
            .AddCookie(options =>
            {
                options.Cookie.Name = "aspnetcorewsfed";
                options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
                options.SlidingExpiration = true;
                options.ExpireTimeSpan = TimeSpan.FromMinutes(Configuration.GetValue<int?>("SessionTimeoutInMinutes") ?? 15);
            })
            .AddWsFederation(options =>
            {
                options.MetadataAddress = "https://localhost:5001/wsfed"; // Address of the Identity Server
                options.RequireHttpsMetadata = false;

                options.Wtrealm = "mvc"; // ClientId registered in Identity Server

                options.CallbackPath = "/";
                options.SkipUnrecognizedRequests = true;
            });
        }

There's documentation on the Identity Server website that describes how to access protected APIs as seen here. But this is using OpenIdConnect. Since I am using WsFederation, I have no clue on how to get the access token or refresh token. Is token refresh impossible with WsFed?

Can anyone point me in the right direction on how to go about this?

1

1 Answers

0
votes

Use OpenIdConnect in the MVC client instead of WsFed. Change the code in the MVC client's Startup.cs to the following:

services.AddAuthentication(options =>{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies").AddOpenIdConnect("oidc", options =>{
    options.Authority = "https://localhost:5001";
    options.ClientId = "mvc-openid";
    options.ClientSecret = "secret";
    options.ResponseType = "code";
    options.SaveTokens = true;
});

The corresponding client registration in Identity Server should be:

new Client {
    ClientId = "mvc-openid",
    ClientSecrets = {
      new Secret("secret".Sha256())
    },
    AllowedGrantTypes = GrantTypes.Code,
    RedirectUris = {
      "https://localhost:6001/signin-oidc"
    },
    AllowedScopes = new List < string > {
      IdentityServerConstants.StandardScopes.OpenId,
      IdentityServerConstants.StandardScopes.Profile,
      "api"
    }
}

https://localhost:5001 is the Identity Server address and https://localhost:6001 is the MVC client address.

The access token for accessing the API can be obtained like so:

var accessToken = await HttpContext.GetTokenAsync("access_token");