ASP.Net Core OpenIdConnect Steam - asp.net-core

I'm trying to implement Steam OpenId into my ASP.Net Core application, and I don't have any previous experience implementing OpenID.
Unfortunately, Steam is massively lacking documentation on their side, and simply state "just download an OpenID library", and provide you with a page to register an API key for a domain name.
There are several implementations available for full ASP.Net, but not for Core, and it seems there are some differences.
I'm trying to use Microsoft.AspNetCore.Authentication.OpenIdConnect, though I am not entirely sure if this is the right library. It seems there is a difference between "OpenID" and "OpenID Connect".
I've set up the authentication in my Startup.cs like so:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
DisplayName = "Steam",
Authority = "http://steamcommunity.com/openid",
ClientId = "MyClientId",
ClientSecret = "ApiKeyHere",
SignInScheme = "SignInCookie",
CallbackPath = new PathString("/Account/SteamCallback"),
RequireHttpsMetadata = false
});
But as soon as I hit the sign-in page, which consists of an action returning a challenge:
public IActionResult SignIn()
{
return Challenge();
}
I get the error
JsonReaderException: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
Newtonsoft.Json.JsonTextReader.ParseValue() InvalidOperationException:
IDX10803: Unable to obtain configuration from: 'http://steamcommunity.com/openid/.well-known/openid-configuration'.
When I look at this URL, it seems to return XML data for the configuration of OpenID:
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://specs.openid.net/auth/2.0/server</Type>
<URI>https://steamcommunity.com/openid/login</URI>
</Service>
</XRD>
</xrds:XRDS>
But the OpenID spec states that this info should be in JSON.
Next I tried registering my own OpenIdConnectMiddleware, much like how this ASP.Net implemtation does it, however, this resulted in not being able to be constructed due to missing services that the OpenIdConnectMiddleware class requires:
System.InvalidOperationException: 'A suitable constructor for type
'TestApplication.SteamOpenId.SteamAuthenticationMiddleware' could not be
located. Ensure the type is concrete and services are registered for
all parameters of a public constructor.'
My implementation:
public class SteamAuthenticationMiddleware : OpenIdConnectMiddleware
{
public SteamAuthenticationMiddleware(
RequestDelegate next,
IDataProtectionProvider dataProtectionProvider,
ILoggerFactory loggerFactory,
UrlEncoder encoder,
IServiceProvider services,
IOptions<SharedAuthenticationOptions> sharedOptions,
IOptions<OpenIdConnectOptions> options,
HtmlEncoder htmlEncoder) :
base(
next,
dataProtectionProvider,
loggerFactory,
encoder,
services,
sharedOptions,
options,
htmlEncoder)
{
}
protected override AuthenticationHandler<OpenIdConnectOptions> CreateHandler() => new SteamAuthenticationHandler();
}
I know this question isn't very specific, but can anyone point me in the right direction with this? I am a little stumped.

It seems there is a difference between "OpenID" and "OpenID Connect".
There is: OpenID 1/2 and OpenID Connect are totally different protocols.
Steam is still using OpenID 2.0 so you can't use ASP.NET Core's OpenID Connect middleware to authenticate your users using their Steam account, as the two protocols are not compatible/interoperable.
I know this question isn't very specific, but can anyone point me in the right direction with this? I am a little stumped.
The aspnet-contrib Steam middleware you've mentioned derives from a generic OpenID 2.0 provider specially developed for ASP.NET Core and thus is probably your best option (that said, I'm the guy who's written it, so I'm likely not objective).
You can find the package on NuGet.org: https://www.nuget.org/packages/AspNet.Security.OpenId.Steam/.

Related

User.Claims is empty after upgrading from ASP.NET Core 3.1 to ASP.NET 5.0

After upgrading from ASP.NET Core 3.1 to version 5, context.User.Claims is empty in
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement)
in
public class MyRequirementHandler : AuthorizationHandler<MyRequirement>
I'm using the Authorization header with a bearer token with JWT. I can see that header being set correctly when looking at the HttpContext.Request.Headers but it doesn't seem to be parsed.
This is set up on a Grpc Service with the [Authorize] attribute.
With ASP.NET Core 3.1, it worked fine. I went through the official migration guide but their references regarding authorisation were only for Azure Active Directory.
I'm using IdentityServer4 which is hosted within that ASP.NET Core app as a middleware (app.UseIdentityServer();)
What did I forget to modify to get ASP.NET Core to parse the authorisation header correctly?
Update:
I checked it in more details and noticed that it's failing because it can't verify the audience (aud) - and yes on the newly created tokens the audience is missing (the old tokens had the audience). Also I noticed that a custom scope, which I was adding in
public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
inside my custom
public class ProfileService : ProfileService<ApplicationUser>
is also missing after the update. This is how the IdentityServer is configured:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, AppIdentityDbContext>()
.AddProfileService<ProfileService>()
.AddInMemoryIdentityResources(AuthResources.GetIdentityResources())
.AddInMemoryApiResources(AuthResources.GetApiResources())
.AddInMemoryClients(TestClientsRequired
? ClientsForTesting.GetTestClients()
: Clients.GetDefaultClients());
After figuring out that the issue might have been due to a missing audience (aud) I looked further and found Missing "aud" claim in access token - the answer was, to explicitly add the audience as a claim and also set the scope one more time, and it worked.
For me this looks the following way:
public static IEnumerable<ApiResource> GetApiResources()
{
yield return ApiResourceBuilder
.IdentityServerJwt(MyWebApiResource)
.AllowAllClients()
.Build()
.AddUserClaims()
.AddScopes(); // <- this is new
}
private static T AddUserClaims<T>(this T resource)
where T : Resource
{
resource.UserClaims.Add(Constants.CustomClaimTypes.MyRoles);
resource.UserClaims.Add(JwtClaimTypes.Audience); // <- this is new
return resource;
}
// this whole method is new ->
private static T AddScopes<T>(this T resource)
where T : ApiResource
{
resource.Scopes.Add(MyWebApiResource);
return resource;
}

Can I add a service info / health check endpoint to my Identity Server 3-based service?

I have a set of AspNet WebApi-based web services and an IdentityServer3-based authentication service. All of the web services support a simple service info endpoint that we use for monitoring and diagnosis. It reports the service version and the server name. The only service that currently does not support the service info endpoint is the IdentityServer3-based authentication service.
Is there a way to add a simple endpoint to an IdentityServer3-based service? In GitHub issue 812 Brock Allen says "We have a way to add custom controllers, but it's undocumented, current unsupported, and not really done." I'd rather not take that indocumented, unsupported route.
Is there a way to modify/extend the discovery endpoint to include additional information?
Here's how I ended up coding this up. At a high level, basically I added a Controllers folder, created a AuthenticationServiceInfoController class with a single GET action method and then registered that controller during Startup. As noted in comment above, my solution had some extra complexity because my AuthenticationServiceInfoController inherited from a base ServiceInfoController defined elsewhere, but I've tried to eliminate that from this sample. So, the controller code looks like this:
[RoutePrefix("api/v1/serviceinfo")]
public class AuthencticationServiceInfoController : IServiceInfoController
{
[Route("")]
[Route("~/api/serviceinfo")]
public IHttpActionResult Get()
{
try
{
ServiceInformation serviceInfo = new ServiceInformation();
serviceInfo.ServiceVersion = Global.serviceVersion;
return Ok(serviceInfo);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
}
It implements a simple interface:
public interface IServiceInfoController
{
IHttpActionResult Get();
}
And in my Startup.Configuration method where I configure Identity Server, I've got:
var idSrvFactory = new IdentityServerServiceFactory();
idSrvFactory.Register(new Registration<IServiceInfoController, Controllers.AuthencticationServiceInfoController>());
I think that's all that it took. It's in place and working in my Identity Server 3-based service.

Claims based authentication, with active directory, without ADFS

I have a client asking for an integrated authentication based solution utilizing a custom role/membership schema. My original plan was to use claims based authentication mechanism with integrated authentication. However, my initial research is not turning up a whole lot of useful information.
To the point, I have an ASP.NET (not core nor owin) WebAPI application, which has api actions used by angular SPA based (asp.net) web application. I am attempting to authorize the api calls using integrated authentication. My initial effort was focused around a custom AuthorizationAttribute and ClaimsAuthenticationManager implementation. However as I got deeper into that I started running into issues with the custom ClaimsAuthenticationManager, at this point I'm not sure that is the proper route to take.
So my question for you all is, can you at least give me some ideas of what it would take to make this happen? I don't need help with secific bits the code, just need to figure out the appropriate "stack" so to speak.
The only real requirement is WebAPI calls can be authorized, with a custom attribute passing a name of a claim to authorize on, but the claim is not in AD even though it is using windows authentication, the claims themselves would come from a database.
Thank you all in advance!
Look at https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api.
Your scenario isn't much different:
you're using AD for authentication
you're using your db for authorization
Simply put this can be addressed by configuring web-api to use windows authentication.
<system.web>
<authentication mode="Windows" />
</system.web>
And add your own IAuthorizationFilter to Web API pipeline, that will check current principal (should be set), and then override this principal with your own (i.e. query db - get claims, and override it with your custom claims principal by setting HttpContext.Current.User and Thread.CurrentPrincipal).
For how to add filter to WebAPI pipe line check out How to add global ASP.Net Web Api Filters?
public class CustomAuthenticationFilter : IAuthenticationFilter {
public bool AllowMultiple { get { return true; } }
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) {
var windowsPrincipal = context.Principal as WindowsPrincipal;
if (windowsPrincipal != null) {
var name = windowsPrincipal.Identity.Name;
// TODO: fetch claims from db (i guess based on name)
var identity = new ClaimsIdentity(windowsPrincipal.Identity);
identity.AddClaim(new Claim("db-crazy-claim", "db-value"));
var claimsPrincipal = new ClaimsPrincipal(identity);
// here is the punchline - we're replacing original windows principal
// with our own claims principal
context.Principal = claimsPrincipal;
}
return Task.FromResult(0);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) {
return Task.FromResult(0);
}
}
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.Filters.Add(new CustomAuthenticationFilter());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute( ... );
}
}
Also there is no need for custom authorization attribute - use default one - its understood by everyone, and makes your code more readable.

Spring security WSO2 IS integration - Figure out how to assign authorities/customize wso2 token

I am very stuck with this issue for a couple of days.
So what I am trying to do is to assign ROLES with spring security framework. My goal is to decode token that I get from WSO2 Identity Server 5.0 through openid and assign the Role so I can authorize the request based on Roles (AUTHORITIES)
This is my SecurityConfig class in simple spring boot app
#Profile("oauth")
#Configuration
#EnableResourceServer
public class SecurityConfig {
}
So, with this configuration, I am able to decode the token.
However, in debug mode, when I made a request with the id_token to the simple spring boot app, I received an error:
java.lang.ClassCastException
java.lang.String cannot be cast to java.util.Collection
And it happens in DefaultAccessTokenConverter class, particularly in the line of code when the map object is converted to String [] roles
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
...
if (user==null && map.containsKey(AUTHORITIES)) {
#SuppressWarnings("unchecked")
String[] roles = ((Collection<String>)map.get(AUTHORITIES)).toArray(new String[0]);
authorities = AuthorityUtils.createAuthorityList(roles);
}
OAuth2Request request = new OAuth2Request(parameters, clientId, authorities, true, scope, resourceIds, null, null,
null);
return new OAuth2Authentication(request, user);
}
This is my WSO2 decoded token
{
"auth_time": 1464819792,
"exp": 1464823490,
"azp": "U1PXsuyV_tdBERmZIoHHnqoGkWIa",
"authorities": "[\"ROLE_ADMIN\",\"approver\",\"Internal\/everyone\"]",
"at_hash": "Hh2LUZl3Bp6yDqyZt4r6Gg",
"aud": [
"U1PXsuyV_tdBERmZIoHHnqoGkWIa"
],
"iss": "https://localhost:9443/oauth2/token",
"locality": "[\"ROLE_ADMIN\"]",
"iat": 1464819890
}
It seems that Spring expects Array of String, not String object (there is a double quote at the beginning and the end of value in authorities.
The aud format seems to be what the spring expects.
So, there are two options I can think o
1. Write some configuration in Spring Oauth2 (I have not figured this out yet)
2. Configure WSO2 Identity Server (This what I've been trying to do).
There are some resources saying that we can implement our own JWTTokenGenerator in WSO2 carbon. From looking at the code, it seems this is where the double quotes are generated in the claim.
org.wso2.carbon.identity.oauth2.authcontext.JWTTokenGenerator
I hope there is someone else who has been going through this.
Thank you very much.
Please find the default implementation here [1]. Also it is better if you can go with IS 5.1.0 for 5.1.0 refer [2]. After building custom JWTTokenGenerator copy it to repository/components/lib. Change
<TokenGeneratorImplClass>
element in identity.xml according to your custom implementation.
[1] https://svn.wso2.org/repos/wso2/carbon/platform/branches/turing/components/identity/org.wso2.carbon.identity.oauth/4.2.3/src/main/java/org/wso2/carbon/identity/oauth2/authcontext/JWTTokenGenerator
[2]https://github.com/wso2/carbon-identity/tree/master/components/oauth/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authcontext
Thank you! That could work too! but for easier implementation, we use 5.2.0 beta version that that produce array of string. T

How can I forms authenticate a user in a custom webservice?

I am working on integrating a silverlight site into our existing application and am trying to get the login functionality working. The Silverlight application needs to have it's own login page, and the login needs to utilize the existing ASP.NET forms authentication. As part of the login procedure, we are calling some external code, so using the scriptable methods that System.Web.ApplicationServices.AuthenticationService exposes is not an option. I tried to use FormsAuthentication.Authenticate to do this, but it didn't work. Does anyone have any ideas on how to get around this?
It sounds as though you need to create a wrapper websevice which can implement the forms authentication support.
This is something I've done so for example I've created a WCF service with the following interface which is referenced by my Silverlight client:
[ServiceContract]
public interface IAuthenticationService
{
[OperationContract()]
string Login(string username, string password, bool isPersistent);
[OperationContract()]
bool Logout();
[OperationContract()]
string IsLoggedIn();
}
and then in my implementation you can call custom code and also use the forms authentication api, for example to login you could have:
try
{
//Call you external code here
//Then use the membership provider to authenticate
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, isPersistent);
}
}
catch (Exception ex)
{
Logging.LogException("Error in Login", ex);
}
Also not you need to include the following attribute above you class definition in your service implementation to have asp.net compat enabled which will give you access to the HttpContext:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
The solution is simple. Just create a custom membership provider that calls your custom code. See this article on MSDN library for more information. There are also full samples available on 15 seconds and a walkthrough video on the ASP.NET website. Finally, it appears Microsoft has released the source for the built-in Membership Provider