Access to API resource with Implicit flow - identityserver4

Does identity server 4 doesn't allow implicit flow to access API Resource.
Identity Server 4 config.cs
new Client
{
ClientId = "implicit",
ClientName = "Implicit Client",
AllowAccessTokensViaBrowser = true,
RedirectUris = { "https://notused" },
PostLogoutRedirectUris = { "https://notused" },
FrontChannelLogoutUri = "http://localhost:5000/signout-idsrv", // for testing identityserver on localhost
AccessTokenLifetime = 10,
AllowedGrantTypes = GrantTypes.Implicit,
AllowedScopes = { "openid", "profile", "email", "ProxyServer", "api" }
}
Api Resouce
new ApiResource("api", "Custom"),
new ApiResource("ProxyServer", "Proxy Server")
In Mvc Client I am using this code ConfigureServices
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.Name = "mvcimplicit";
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = Constants.Authority;
options.RequireHttpsMetadata = false;
options.ClientId = "implicit";
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("ProxyServer");
options.Scope.Add("profile");
options.Scope.Add("email");
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
};
});
When I try in browser I get "Sorry, there was an error : invalid_scope ". But if I remove options.Scope.Add("ProxyServer"); it works fine and Identity server 4 take me to login page.

OK I found the issue but posting just in case someone else face the same problem.
Response type needs to be specified explicitly otherwise it wont work.
options.ResponseType = "id_token token";
Modify the response type accordingly.

Related

IdentityServer4 External Authentication (Azure Active Directory) fails

I created project from template: "is4aspid" and added Azure Active Directory authentication.
In Startup.cs added this code:
// preserve OIDC state in cache (solves problems with AAD and URL lenghts)
services.AddOidcStateDataFormatterCache();
// cookie policy to deal with temporary browser incompatibilities
services.AddSameSiteCookiePolicy();
services.AddAuthentication()
.AddOpenIdConnect("aad", "Sign-in with Azure AD", options =>
{
options.Authority = "https://login.microsoftonline.com/common";
options.ClientId = "myClientId";
options.ClientSecret = "myClientSecret";
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.ResponseType = "id_token";
options.CallbackPath = "/signin-aad";
options.SignedOutCallbackPath = "/signout-callback-aad";
options.RemoteSignOutPath = "/signout-aad";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
NameClaimType = "name",
RoleClaimType = "role"
};
});
I added redirect uri on Azure: http://localhost:5000/signin-aad
Error occure where Callback method is called.
HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme) Success is always false
[HttpGet]
public async Task<IActionResult> Callback()
{
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
// some code
}
Where is my error?
Solution
Must define IdentityConstants.ExternalScheme in startup and callback

Identity Server 4 and User.Claims.Properties.Count always is 0 on client

I need to fill in the "Properties" in the client's claim.
I am writing down a claimon the IS4 server in the ProfileService class:
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// ...
Claim claim = new Claim("userData", "personalRights");
string valuePersonalRights = JsonConvert.SerializeObject(userRights);
claim.Properties.Add(GetKeyValuePair("rights", valuePersonalRights));
claims.Add(claim);
context.IssuedClaims.AddRange(claims);
}
private KeyValuePair<string, string> GetKeyValuePair(string key, string value)
{
KeyValuePair<string, string> keyValuePair = new KeyValuePair<string, string>(key, value);
return keyValuePair;
}
In this claim on the server there are records "Properties":
https://postgres-russia.ru/wp-content/files/is4_img/on_server.jpg
However, on the client, the properties of this claim are missing:
https://postgres-russia.ru/wp-content/files/is4_img/on_client.jpg
Client Configuration:
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("domainGroups");
options.Scope.Add("geolocation");
options.Scope.Add("fullname");
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
};
});
How to get claims properties on the client?
When you are defining your client, you can assign it's claims too, which will be included in access token.
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "spa",
ClientName = "SPA Client",
ClientUri = "",
AllowedGrantTypes = {GrantType.ResourceOwnerPassword,GrantType.ClientCredentials},
RedirectUris =
{
},
RequireClientSecret = false,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
PostLogoutRedirectUris = { "" },
AllowedCorsOrigins = { "","" },
AllowedScopes = { "openid", "profile","roles", IdentityServerConstants.LocalApi.ScopeName },
Claims = new Claim[]//look at this property
{
new Claim("prop1","value1")
}
}
};
This is a common problem when using Net Core. For some reason, the Firefox browser was simply silent, without showing errors, and the Chrome browser pointed to 431 Request Header Fields Too Large. The size of the statements in the cookie was over 4096 byte. Solved by using the ITicketStore and storing the claims as claims in the user session. More details: stackoverrun.com/ru/q/11186809
More: IIS Deployed ASP.NET Core application giving intermittent 431 Request headers too long error

Wobbly connection between javascript SignalR client and .net core backend

My JavaScript SignalR client routinely connects to my asp.net core implementation of IdentityServer4.
On the client I see:
Information: WebSocket connected to wss://localhost:5001/signal/satellite?
idsrv.session=4ec93d2f4c3b9a970ff82a537ae04d97&id=0eUBg2-xobp4CNttuIQJcg
But, it also doesn't connect much of the time, with an error that there was an unexpected token in JSON at position 0.
It seems like it runs well for a bit and then poorly, even if I re-login, it still throws the error. In the browser, the call always looks more or less like:
https://localhost:5001/signal/satellite?
idsrv.session=89364018a975e4fefff9ad0869b1ae09
I don't seem to need the middleware for it to move the querystring value into the header, I still get the user in my hub during onConnect with their sub claim, etc. However, if I do put in the middleware before app.UseAuthentication() and watch for:
context.Request.QueryString.Value
when it successfully handshakes with the server, I see:
idsrv.session=89364018a975e4fefff9ad0869b1ae09
but when it doesn't, I see this:
?client_id=internal&redirect_uri=https%3A%2F%2Flocalhost%3A5001%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20offline_access%20Harvey&response_mode=form_post&nonce=636817245670169226.ZWM2OWQ0ZWEtOTQzMC00YTJlLWI4MzQtYmIxZDZjOWVlYjg5ZTA4NTU2M2QtNjczZi00MTlmLThjYmQtZWUzZTQ1ODMzNDQ0&state=CfDJ8MCxOjJiJLdKqZrnPwOHDhqMnzWz6MqRb03SxToClqQ1F3n9g8yLdW683HRpZSHd-5wkN-6je4tHJkA8sc5i6YoKRxtMHwnWqxVW5-nXFaaH0TfOLUeqfxDzXLxnftmWFXLjK3Y7b6R2WzcDLEjChU1_Fk6X64SAHNRqeizGDPzRhxpV0U5w19Bbt7pUyRbYymn2WNedCS1F7g_wtwtJXDjCzWKBxqvPZ5Dtg99gxKkANalKYs7C4-fm7YdD0gFvsuV4CXsu0T06MjzID_zpA_F7TmSue4vGI-0_qY55Swc5mbLWUwKHtj6ZTfOG4UmTEP_hbj2PO9w2oNg9TWqTPtDC3-qSl1fTUkY0EtCwbA7F&x-client-SKU=ID_NETSTANDARD1_4&x-client-ver=5.2.0.0
so I'm stuck either not needing to do it because it succeeds on its own or the querystring never makes it to the middleware to move over (which is probably why it can't do it on its own)
What am I missing for it to consistently work?
Add Hybrid Client to IdentityServer4:
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
and then in startup on the mvc client:
ServiceCollection.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "https://localhost:5001";
options.RequireHttpsMetadata = false;
options.ClientSecret = "secret";
options.ClientId = "mvc";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("offline_access");
options.Scope.Add("api1");
options.ClaimActions.MapJsonKey("website", "website");
});
This is modeled after the example: Quickstart5_HybridAndApi
In SignalR on server:
[Authorize]
public class SignalRSignalHub : Hub
{
public override Task OnConnectedAsync()
{
var context = Context.GetHttpContext();
return base.OnConnectedAsync();
}
}
SignalR JavaScript Client:
self.signalRConnection = new signalR.HubConnectionBuilder()
.withUrl("/signal/satellite?" + document.cookie).build();

IdentityServer4 fails to authenticate in memory users

We have an existing ID server configuration and I am trying to work on the UI without having to set up the rest of the connections. I followed the example to set up an MVC client:
In Startup.cs:
Console.WriteLine("Adding Test Users!!!");
serviceBulder.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
New client added:
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
Users are configured as the following:
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password"
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password"
},
};
}
MVC is configured to use the services:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services
.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
});
}
I have a couple of issues:
When I use AllowedGrantTypes = GrantTypes.Implicit per tutorial I get "Sorry, there was an error : unauthorized_client"
When I use the AllowedGrantTypes = GrantTypes.Hybrid I do get the login screen but my in memory users (alice and bob with pw = password) can't log in.
Questions:
- Does AddTestUsers not work when there are additional data stores present already?
- Why does my grant type differ from the walkthrough?
I'd check on the AllowedGrantTypes at the identity server level. We have roughly the same setup and we use AllowedGrantTypes = GrantTypes.HybridAndClientCredentials This allows the MVC application to talk with identity server behind the scenes.
The test users should be present.

IdentityServer4 with Windows authentication and custom claims

I have a problem with windows auth and custom claims.
I have an identityServer with windows auth and User.Identity.Name show my AD name.
But I cannot understand, how should I add some properties from my storage to this user. I have now something like this:
var claims = new List<Claim> {
new Claim("devhomepage", "www.devsite.com", ClaimValueTypes.String)};
var userIdentity = new ClaimsIdentity(claims, "siteinfo");
User.AddIdentity(userIdentity);
await HttpContext.SignInAsync(User);
return RedirectToLocal(returnUrl);
and it doesn't work :-) my client will be not authorized.
here is a config for the server
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = {"http://localhost:60640/signin-oidc"},// where to redirect to after login
PostLogoutRedirectUris = {"http://localhost:60640/signout-callback-oidc"},// where to redirect to after logout
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
},
AllowOfflineAccess = true,
RequireConsent = false
and is't a client
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:49245/";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
});
For SignIn I must use Microsoft.AspNetCore.Identity.SignInManager class with method SignInAsync(/*I should take hire a TUser object, that I stored in my database, and make mapping with my AD account */)
To use custom claims (will be used for all samples with custom claims in IS4):
public class ProfileService : IProfileService
{
protected UserManager<IdentityUser> _userManager;
public ProfileService(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//>Processing
var user = await _userManager.GetUserAsync(context.Subject);
//my custom claims
var claims = new List<Claim>
{
new Claim("devhomepage", "www"),
new Claim("reporting","reps")
};
context.IssuedClaims.AddRange(claims);
}
public async Task IsActiveAsync(IsActiveContext context)
{
//>Processing
var user = await _userManager.GetUserAsync(context.Subject);
context.IsActive = (user != null);
}
}
But this service I must register only after my IdentityService
services.AddIdentityServer()
...
services.AddTransient<IProfileService, ProfileService>();
}
of ConfigureServices

Resources