I have a project done with Asp.Net Core 2.0 API, Identity Server and WPF app.
I am able to access the API from WPF after I login in.
Now I am trying to implement roles so I can be able to authorize just certain users to access the API.
In Config.cs I am declaring my Client and add to the scope :
new Client
{
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"fiver_auth_api",
"role"
},
AlwaysIncludeUserClaimsInIdToken=true
}
Declaring TestUser:
return new List<TestUser>
{
new TestUser
{
SubjectId = "", Username = "", Password = "",
Claims = new List<Claim>
{
new Claim(JwtClaimTypes.Email, "AliceSmith#email.com"),
new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.Role, "Admin"),
new Claim(JwtClaimTypes.Scope, "openid offline_access fiver_auth_api")
}
}
}
And in the controller I am using :
[Authorize(Roles = "Admin")]
Why I don`t get the user claims in the token?
For who is interested there is how I fixed it:
In your configuration file add a list for your roles:
new ApiResource
(
"fiver_auth_api",
"Fiver.Security.AuthServer.Api",
new List<string> {"role"} <--- Add this line to your API
)
Related
I am implementing Identity Server in a razor page application.
When requesting the speech ApiResource, identityserver returns "invalid_scope". My understanding is that the resource is a group of scopes. So, I was expecting the identityserver to return the scopes defined in the speech resource.
Note: Which I add speech as ApiScope it works fine but then it does not add the speech.synthesize and payment.subscription scopes.
Here's how I have defined the ApiScopes:
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("speech.synthesize", "Speech synthesis",new []{"api.create" }),
new ApiScope("payment.subscription", "Subscription service"),
new ApiScope("payment.manage", "Manage Payment"),
};
And here's how I have defined the ApiResource:
public static IEnumerable<ApiResource> ApiResources =>
new List<ApiResource>
{
new ApiResource("speech", "Speech API")
{
Scopes = { "speech.synthesize", "payment.subscription" }
}
};
And here's the client configuration:
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
AlwaysSendClientClaims = true,
// scopes that client has access to
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"speech"
}
}
};
What is wrong here? Can anybody help me understand the problem.
What is the role of the Api Resource if not grouping the scopes.
You as a client asks for ApiScopes, not ApiResources. One more more ApiResource can point to an ApiScope.
An ApiResource represents an API instance, not a Scope. ApiResources are like clients, but for Apis.
See my answer here for more details about the difference between IdentityResource, ApiResource and ApiScope
I have been searching for how IdentityServer4 uses DB. I have read: https://docs.identityserver.io/en/release/quickstarts/8_entity_framework.html and looked at the QuickStart4 which uses a DB store. What I can't find is how I can use it in a many clients scenario where we want to add client details to DB only without having to add the client to the config.cs in Identity Server like so:
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};
public static IEnumerable<Client> Clients =>
new List<Client>
{
// machine to machine client
new Client
{
ClientId = "client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,
// scopes that client has access to
AllowedScopes = { "api1" }
},
// interactive ASP.NET Core MVC client
new Client
{
ClientId = "mvc",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
// where to redirect to after login
RedirectUris = { "https://localhost:5002/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
}
};
}
}
see this page Entity Framework Integration and this page Entity Framework Support
Basically, what you need to do is:
Add this NuGet package IdentityServer4.EntityFramework
Apply the migrations to create the necessary tables or use the pre-made SQL scripts here
Add the AddConfigurationStore to your startup class
Alternatively, you implement your own IClientStore implementation.
I have an Api with Name MyApi and I use another asp.net core application with Identityserver4 for Protect MyApi,Now I don't have any problem in MyApi but,I want to save my Users's NationalCode ,So I should save this in my IdentityServer Database,But can't Get UserId (with User.Identity.Name) in my IdentityServer Project,I had same problem in my previose question
User.Identity.Name is null in my ASP.NET Core Web API
Now I have this problem in my IdentityServer4 project,So
Can I use Of MyApi token Or I should get a new token for send request to my idenittyserver4 project?
If I can MyAPI token ,How should I add configuration to solve the problem?
If I should take new token for my IdentityServer4 project,DO I need to want users to login again?!!!
Edit
I found a tutorail in below link but My Problem not solved Yet.
http://docs.identityserver.io/en/latest/topics/add_apis.html
I have seed my IdentityDatabase with below method
public async Task AddIdenityServerApiToResources(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
var ccontext = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
ccontext.Database.Migrate();
//=============================================================
ccontext.ApiResources.Add(new ApiResource(IdentityServerConstants.LocalApi.ScopeName) {
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Subject,
JwtClaimTypes.Role,
}
}.ToEntity());
//Add ApiResource To Client's Scope
var Clients = ccontext.Clients.Include(e => e.AllowedScopes);
foreach (var item in Clients)
{
item.AllowedScopes.Add(new IdentityServer4.EntityFramework.Entities.ClientScope() { Scope = IdentityServerConstants.LocalApi.ScopeName });
}
var Count = await ccontext.SaveChangesAsync();
if (Count > 0)
{
}
}
}
In IdentityServer4 startup.cs ConfigureServices
You should treat the api as if it's like any other api that needs to be secured by idserver4.
meaning: use AddAuthentication and AddJWTToken:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https:// idserver4 ";
options.RequireHttpsMetadata = true;
options.Audience = "api name";
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
in API controller :
use Authorize Attirbute and determine the authentication scheme like this:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
I solved the problem with below link:
http://docs.identityserver.io/en/latest/topics/add_apis.html
But the problem was where that I don't used Authorize on my controller with LocalApi.PolicyName policy
[Route("localApi")]
[Authorize(LocalApi.PolicyName)]
public class LocalApiController : ControllerBase
{
public IActionResult Get()
{
// omitted
}
}
after that the prolem was solvled
I have set-up identityserver3 and MVC4 client using this tutorial. When I configured client to use 'Implicit' flow things are working as expected and I am getting back 'profile' scope. i.e. I can find claims first_name and given_name. Below my configuration code.
Client and User configuration
public static class Users
{
public static List<InMemoryUser> Get()
{
return new List<InMemoryUser>
{
new InMemoryUser
{
Username = "Bob",Password = "password",Subject = "1",
Claims = new []
{
new Claim(Constants.ClaimTypes.GivenName,"firstName"),
new Claim(Constants.ClaimTypes.FamilyName,"lastName")
}
}
};
}
}
public static class Clients
{
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
ClientId = "MVC",
ClientName = "MVC Client Name",
RedirectUris = new List<string>
{
"https://localhost:44302/"
},
Flow = Flows.Implicit,
AllowAccessToAllScopes = true
}
};
}
}
Identity Server Configuration
public void Configuration(IAppBuilder app)
{
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.Map("/identity", appBuilder => {
appBuilder.UseIdentityServer(new IdentityServer3.Core.Configuration.IdentityServerOptions
{
SiteName = "Site Name",
SigningCertificate = LoadCertificate(),
RequireSsl = false,
Factory = new IdentityServer3.Core.Configuration.IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryUsers(Users.Get())
.UseInMemoryScopes(StandardScopes.All)
});
});
app.UseCookieAuthentication(new Microsoft.Owin.Security.Cookies.CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44302/identity",
ClientId = "MVC",
RedirectUri = "https://localhost:44302/",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
Scope = "openid profile"
});
}
In my MVC application I have secured Action on Home controller named 'Contact'
[Authorize]
public ActionResult Contact()
{
ClaimsPrincipal principal = User as ClaimsPrincipal;
return View(principal.Claims);
}
And finally here is simple view
#model IEnumerable<System.Security.Claims.Claim>
#foreach (var item in Model)
{
<div>
<span>#item.Type</span>
<span>#item.Value</span>
</div>
}
</div>
Now when I run this app, after clicking on secure 'Contact' link I am being redirected to STS server and after providing credentials I can see below output.
Note that claims given_name and family_name exists in the claims returned by STS.
Problem:
The moment I change Client to support Hybrid flow. I am not getting back claims given_name and family_name
I made below changes to my code.
Client configuration
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
ClientId = "MVC",
ClientName = "MVC Client Name",
RedirectUris = new List<string>
{
"https://localhost:44302/"
},
Flow = Flows.Hybrid,//Changed this to Hybrid
AllowAccessToAllScopes = true
}
};
}
Server Configuration
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44302/identity",
ClientId = "MVC",
RedirectUri = "https://localhost:44302/",
ResponseType = "code id_token token", //Changed response type
SignInAsAuthenticationType = "Cookies",
Scope = "openid profile"
});
After running applicaton I can see below claims returned by STS
Note that claims given_name and family_name are missing this time.
Have I missed anything?
When you only ask for an id_token all the claims for the user are in the id_token. When you change your request to get a token (either by asking for code or token) then only the user claims configured as "AlwaysInclude" are included in the id_token. The rest must be retrieved from the user info endpoint using the access_token you received. You can use the helper APIs in the IdentityModel library to easily access the user info endpoint. Our samples show how you can do this: https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/MVC%20OWIN%20Client%20(Hybrid)/Startup.cs#L66
I have ASP.NET Core app with angular 2 front-end. I use cookie auth.
But I want to split my app into 2 separate sites - one front-end site on angular2 and one back-end site on asp.net core.
How do I use auth from ASP.NET Core site to authenticate front-end app?
There's a login page in my back-end site. How do I identify in front-end app that I'm not authenticated, then redirect to back-end app and then get auth cookies? I'm not sure I understand mechanic of this process.
I used token based authentication. I choosed this solution: https://stormpath.com/blog/token-authentication-asp-net-core & https://github.com/nbarbettini/SimpleTokenProvider
For Authentication I prefer to use cookies.
Use cookie authentication without Identity
Login Code
[HttpPost("login")]
[AllowAnonymous]
public async Task<HttpBaseResult> Login([FromBody]LoginDto dto)
{
var user = db.Users.Include(u=>u.UserRoles).SingleOrDefault();
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName)
};
var roles = user.UserRoles.Select(u => u.Role);
foreach (var item in roles)
{
claims.Add(new Claim(ClaimTypes.Role, item.Name));
}
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = dto.RememberMe });
// ...
}
Cross Domain
ConfigureServices
{
options.SlidingExpiration = true;
options.Cookie.HttpOnly = false;
// Dynamically set the domain name of the prod env and dev env
options.Cookie.Domain = Configuration["CookieDomain"];
});
Configure
app.UseCors(builder => builder.WithOrigins("http://localhost:4200", "http://www.example.com","http://example.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
Angular Code
public login(userName: string, password: string, rememberMe: boolean): Observable<HttpBaseResult> {
const url: string = `${this.url}/login`;
var data = {
UserName: userName,
Password: password,
RememberMe: rememberMe
};
return this.client.post<HttpBaseResult>(url, data, { withCredentials: true });
}