Access token for API controller inside Identity server itself - asp.net-core-mvc

I have created an identity server 4 project and a mvc client project. Authentication flow works as expected. I have added an API controller in the same project as identity server and i want to hit this api resource from mvc client.Essentially,i need both identity server middleware and token validation middle inside the idenity server project.

If you haven't already, add these Nuget packages to your already established IdentityServer app/site:
IdentityServer4.AccessTokenValidation
Microsoft.AspNetCore.Mvc
Add another Api Resource to your resources list:
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API"),
new ApiResource("api2", "IdentityServer API")
};
}
Update your client configuration to allow api2:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "mvc",
... omitted
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api2"
}
}
};
}
In the IdentityServer's Configure method in Startup add:
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ApiName = "api2"
});

Related

Can I change Identity Providers with OWIN and OpenID at run time?

I am using OWIN middleware to configure OpenID Authentication. This configuration is called at StartUp.cs points to a B2C IDP.
public void ConfigureAuth(IAppBuilder app)
{
// Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// ASP.NET web host compatible cookie manager
CookieManager = new SystemWebChunkingCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(Globals.WellKnownMetadata, Globals.Tenant, Globals.DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = Globals.ClientId,
RedirectUri = Globals.RedirectUri,
PostLogoutRedirectUri = Globals.RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claim type that specifies the Name property.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
ValidateIssuer = false
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"openid profile offline_access {Globals.ReadTasksScope} {Globals.WriteTasksScope}",
// ASP.NET web host compatible cookie manager
CookieManager = new SystemWebCookieManager()
}
);
}
How can I get the middleware to use different configurations, specifically for the object OpenIdConnectAuthenticationOptions, in order to point to a different IDP at runtime?
You can register multiple named openIDCConnect handler like
.AddOpenIdConnect("Auth0", options =>
{ Options...
}
.AddOpenIdConnect("google", options =>
{ Options...
}
.AddOpenIdConnect("facebook", options =>
{ Options...
}
Then the user can choose how he wants to authenticate, using one of :
HttpContext.SignInAsync("Auth0",....);
HttpContext.SignInAsync("google",....);
HttpContext.SignInAsync("facebook",....);
When you add multiple handlers, you need to make sure the local callback path in the client is different for each handler, like
CallbackPath = new PathString("/signin-auth0");
CallbackPath = new PathString("/signin-google");
CallbackPath = new PathString("/signin-facebook");
(You set this in the options)

Adding Claims to Identity from my WebApi

I am currently developing a Web Application that contains a Angular4 UI working with IdentitServer4 and a WebApi dotnetcore application.
We have the application authentication mechanism working with IDS but we now want to limit parts of our Angular application based on what permissions a user has granted them. This information is stored behind our WebApi. These permissions would also be used to secure our WebApi from stopping users doing particular actions if they aren't allowed i.e. EditUsers.
the problem I am facing is that ideally after being authentication by IdentityServer I would like the Angular application to fetch the list of allowed
permissions and from there they send those up to the WebApi as part of their claims. The reason for this is that I don't want to have to query the database
on each Api Call if I can help it just to see if a particular user has access to a particular Controller action.
Is there anyway that I can set these claims so that subsequent calls to the API contains these and from there I can just check the claim information on the User Claim Identity
to verify access to a resource?
You can add scope to your WebApi (official docs) for example
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "https://demo.identityserver.io",
ApiName = "api1",
AllowedScopes = { "api1.read", "api1.write" }
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
And you can add claims to the client application as:
var mvcClient = new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
ClientUri = "http://identityserver.io",
AllowedGrantTypes = GrantTypes.Hybrid,
AllowOfflineAccess = true,
ClientSecrets = { new Secret("secret".Sha256()) },
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/" },
LogoutUri = "http://localhost:5002/signout-oidc",
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api1", "api2.read"
},
};
This is application base, for assigning permissions per user, you can define roles in your scopes for the user and can then decorate your controller or methods with that Role for ex:
For admin: new Claim("role","Admin")
For guestuser: new Claim("role","guest")
[HttpGet]
[Authorize(Roles = "Admin")]
public IActionResult Edit()
{
//whatever
}
[Authorize(Roles = "Guest")]
[HttpGet]
public IActionResult View()
{
//whatever
}

asp.net web form client with identity server 4

I have a asp.net solution which consists of
1). asp.net identity server rc 3
2). asp.net Core web api
3). asp.net webform ( not in asp.net core, client)
I don't see any sample with identity server 4 and web form client. Can you please suggest how to authenticate web form user using identity server with asp.net identity and then call api with the access token ?
I don't see identity server 4 sample with web form client or sample
identity server 3 has a sample but it is doing everything in startup
When i see mvc client for identity server 4, it has all settings in configure method and then calls it like this
How will i apply Authorize attribute in webform so that i am redirected to identity server 4 for login and then after login when i call api like this:
how to change client for webform ?
new Client()
{
ClientId = "mvcClient",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets = new List<Secret>()
{
new Secret("secret".Sha256())
},
RequireConsent = false;
// where to redirect to after login
RedirectUris = { "http://localhost:5002/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:5002" },
AllowedScopes =
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
StandardScopes.OfflineAccess.Name,
StandardScopes.Roles.Name,
"API"
}
}
new InMemoryUser()
{
Subject = "1",
Username = "testuser",
Password = "password",
Claims = new List<Claim>()
{
new Claim("name", "Alice"),
new Claim("Website", "http://alice.com"),
new Claim(JwtClaimTypes.Role, "admin")
}
}
return new List<Scope>()
{
StandardScopes.OpenId, // subject id
StandardScopes.Profile, // first name, last name
StandardScopes.OfflineAccess,
StandardScopes.Roles,
new Scope()
{
Name = "API",
Description = "API desc",
Type = ScopeType.Resource,
Emphasize = true,
IncludeAllClaimsForUser = true,
Claims = new List<ScopeClaim>
{
new ScopeClaim(ClaimTypes.Name),
new ScopeClaim(ClaimTypes.Role)
}
}
};
public void CallApiUsingClientCredentials()
{
var tokenClient = new TokenClient("http://localhost:5000/connect/token", "mvc", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var content = await client.GetStringAsync("http://localhost:5001/identity");
var result = JArray.Parse(content).ToString();
}
[Authorize(Roles="admin)]
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
Late answer, but hopefully it helps someone, still supporting web forms.
There is no problem to use startup together with web forms. The only limitation is no place for AuthorizeAttribute there, but it's still not a problem, just put:
app.UseStageMarker(PipelineStage.Authenticate);
at the bottom of your
public void Configuration(IAppBuilder app)
method within OWIN Startup.An example Startup implementation could be fetched from my github. It works with MVC, Web Forms and additionally brings JWT validation from IdentityServer v.3' codebase, upgraded to compile with the latest OWIN libraries.
If I still left anything unclear, don't hesitate to ask in the comments.

CORS error on requesting OWIN token

I need to implement OWIN authorization from web api server. Below is my startup class.
[assembly: OwinStartup(typeof(SpaServerSide.MyStartUp))]
namespace SpaServerSide
{
public class MyStartUp
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
app.Map("/signalr", map =>
{
map.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
var hubConfig = new Microsoft.AspNet.SignalR.HubConfiguration { };
map.RunSignalR(hubConfig);
});
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/#")
});
OAuthAuthorizationServerOptions OAuthOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new SpaServerSide.Shared.OAuthTokenServer()
};
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
}
}
Then, I implement the OAuthAuthorizationServerProvider as the following :
public class OAuthTokenServer : OAuthAuthorizationServerProvider
{
public ASPIdentityUserManager cusUserManager;
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
var user = await cusUserManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "Username and password do not match.");
return;
}
var identity = await cusUserManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
context.Validated(identity);
}
}
After that, I have hosted the web server on http://localhost:5587 and client web site on http://localhost. When I tried to request the token using Angular JS, the browser threw me an CORS error. The message is as follows :
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://localhost:5587/Token. (Reason: CORS
header 'Access-Control-Allow-Origin' missing).
Please suggest me anything I would have missed.
Move the line:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
at the beginning of your Configuration() method.
You have to configure CORS middleware before oauth middleware. And before signalr middleware if you need it.
Try this
Enable browser setting for allowing cross origin access
IE: http://www.webdavsystem.com/ajax/programming/cross_origin_requests
Firefox: How to enable CORS on Firefox?
Chrome: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en
I think u need enable CORS in your server side. U can refer to this http://enable-cors.org/server.html . Click link based on your server.
Hope that help u. :)

IdentityServer3 and Web API in same process

I have Asp.net MVC web application and webapi in same project.I am using Resource owner Credential flow to have identity management.Is it possible to configure webapi, client and identity server in same startup.cs(startup.cs of webapplication).While trying to configure webapi and identity server in same startup.cs i ended up with the following error "An exception of type 'System.InvalidOperationException' occurred in IdentityServer3.AccessTokenValidation.dll but was not handled in user code
Additional information: IDX10803: Unable to create to obtain configuration from: 'https://localhost:44303/.well-known/openid-configuration'."
Here is the code of my startup.cs:
using IdentityServer3.AccessTokenValidation;
using IdentityServer3.Core.Configuration;
using IdentityServer3.Core.Services;
using IdentityServer3.Core.Services.Default;
using MarilynIdentityServer.IdentityServer;
using Microsoft.Owin;
using Owin;
using System;
//using System.IdentityModel.Claims;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Web.Http;
using System.Linq;
[assembly: OwinStartupAttribute(typeof(MarilynIdentityServer.Startup))]
namespace MarilynIdentityServer
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
var userService = new UserLoginService();
factory.UserService = new Registration<IUserService>(resolver => userService);
factory.CorsPolicyService = new Registration<ICorsPolicyService>(new DefaultCorsPolicyService { AllowAll = true });
var option = new IdentityServerOptions
{
SiteName = "Embedded IdentityServer",
SigningCertificate = LoadCertificate(),
Factory = factory,
//AuthenticationOptions = new AuthenticationOptions
//{
// //EnableLocalLogin = false,
// IdentityProviders = ConfigureIdentityProviders
//},
};
app.UseIdentityServer(option);
app.Map("/api", idsrvApi =>
{
// token validation
idsrvApi.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44303/",
RequiredScopes = new[] { "sampleApi" }
});
// add app local claims per request
idsrvApi.UseClaimsTransformation(incoming =>
{
// either add claims to incoming, or create new principal
var appPrincipal = new ClaimsPrincipal(incoming);
incoming.Identities.First().AddClaim(new Claim("appSpecific", "some_value"));
return Task.FromResult(appPrincipal);
});
// web api configuration
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
idsrvApi.UseWebApi(config);
});
}
X509Certificate2 LoadCertificate()
{
return new X509Certificate2(
string.Format(#"{0}bin\identityServer\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");
}
}
}
To conclude Is it possible to configure WebApi, Webapplication and Identity Server in same application?
Any help would be appreciated.
Regards
Amit
It definitely is possible, however I have experienced similar issues to yourself in not being able to find the local Identity Server.
What usually solves it is modifying your pipeline to have Identity Server in it's own app.Map block instead of the root. This seems to make it discoverable by the authentication middleware.
If you try this, don't forget to update the authority in you authentication middleware.
I achieved the goal stated by this question using a combination of Scott Brady's post and Andras Nemes's post, both excellent. Here's the Startup.cs that worked for me:
using IdentityServer3.Core.Configuration;
using Owin;
using System.Web.Http;
namespace idsvr3owin
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// kick the Web API piece of this app
app.Map(
"/api",
apiApp => {
HttpConfiguration httpConfiguration = new HttpConfiguration();
// api.App_Start is a namespace in my app
api.App_Start.WebApiConfig.Register(httpConfiguration);
apiApp.UseWebApi(httpConfiguration);
}
);
// kick the IdentityServer3 piece of this app
app.Map(
"/identity",
identityApp =>
{
identityApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Standalone Identity Server",
// idsvr3 is a namespace in my app
SigningCertificate = idsvr3.Cert.LoadCertificate(),
Factory = new IdentityServerServiceFactory()
.UseInMemoryClients(idsvr3.Clients.Get())
.UseInMemoryScopes(idsvr3.Scopes.Get())
.UseInMemoryUsers(idsvr3.Users.Get()),
RequireSsl = true
});
}
);
}
}
}

Resources