.Net Core 3.1 MVC OAuth Server - oauth-2.0

I am trying to migrate OAuth server code written for .Net Framework 4.5 to .Net Core 3.1. The original code pretty much looks like this. It implements the IAuthorizationServerHost interface. It makes sense to me that it has some functions that the server should implement (like CreateAccessToken that generates and returns a token).
But for .Net Core 3.1, DotNetOpenAuth is not available. So I searched around and found apparently the best solution IdentityServer4 along with many other tutorials (Tut1 Tut2 etc.). But all those looked to me as if they have just implemented the Identity Server (basically just login, logout and register). I don't see how is the token been issued.
My controller class in .Net Framework 4.5 looks like this. A successful login is usually followed by oauth/authorize route that provisions the token and redirects back to the third party.
public class OAuthController : Controller
{
// OAuth2AuthorizationServer is an implementation if IAuthorizationServerHost
private readonly AuthorizationServer authorizationServer = new AuthorizationServer(new OAuth2AuthorizationServer());
public ActionResult Token()
{
var result = this.authorizationServer.HandleTokenRequest(this.Request).AsActionResult();
return result;
}
[Authorize, AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Authorize()
{
var request = this.authorizationServer.ReadAuthorizationRequest(this.Request);
if (request == null)
{
throw new HttpException((int)HttpStatusCode.BadRequest, "Bad request");
}
var response = authorizationServer.PrepareApproveAuthorizationRequest(request, User.Identity.Name);
return authorizationServer.Channel.PrepareResponse(response).AsActionResult();
}
}
I am new to this authentication stuff - have a quite hollow understanding of the concepts. I am struggling to understand the OAuth server related code flow in these tutorials.
So how can I use those packages to create an OAuth server in .Net Core 3.1?

Related

Sustainsys SAML2 Sample for ASP.NET Core WebAPI without Identity

Does anyone have a working sample for Sustainsys Saml2 library for ASP.NET Core WebAPI only project (no Mvc) and what's more important without ASP Identity? The sample provided on github strongly relies on MVC and SignInManager which I do not need nor want to use.
I added Saml2 authentication and at first it worked fine with my IdP (I also checked the StubIdP provided by Sustainsys) for first few steps so:
IdP metadata get properly loaded
My API properly redirects to sign-in page
Sign-in page redirects to /Saml2/Acs page, and I see in the logs that it parses the result successfully
However I don't know how to move forward from there and extract user login and additional claims (my IdP provided also an e-mail, and it is included in SAML response which I confirmed in the logs).
Following some samples found on the web and modyfing a little bit the MVC Sample from GitHub I did the following:
In Startup.cs:
...
.AddSaml2(Saml2Defaults.Scheme,
options =>
{
options.SPOptions.EntityId = new EntityId("...");
options.SPOptions.ServiceCertificates.Add(...));
options.SPOptions.Logger = new SerilogSaml2Adapter();
options.SPOptions.ReturnUrl = new Uri(Culture.Invariant($"https://localhost:44364/Account/Callback?returnUrl=%2F"));
var idp =
new IdentityProvider(new EntityId("..."), options.SPOptions)
{
LoadMetadata = true,
AllowUnsolicitedAuthnResponse = true, // At first /Saml2/Acs page throwed an exception that response was unsolicited so I set it to true
MetadataLocation = "...",
SingleSignOnServiceUrl = new Uri("...") // I need to set it explicitly because my IdP returns different url in the metadata
};
options.IdentityProviders.Add(idp);
});
In AccountContoller.cs (I tried to follow a somewhat similar situation described at how to implement google login in .net core without an entityframework provider):
[Route("[controller]")]
[ApiController]
public class AccountController : ControllerBase
{
private readonly ILog _log;
public AccountController(ILog log)
{
_log = log;
}
[HttpGet("Login")]
[AllowAnonymous]
public IActionResult Login(string returnUrl)
{
return new ChallengeResult(
Saml2Defaults.Scheme,
new AuthenticationProperties
{
// It looks like this parameter is ignored, so I set ReturnUrl in Startup.cs
RedirectUri = Url.Action(nameof(LoginCallback), new { returnUrl })
});
}
[HttpGet("Callback")]
[AllowAnonymous]
public async Task<IActionResult> LoginCallback(string returnUrl)
{
var authenticateResult = await HttpContext.AuthenticateAsync(Constants.Auth.Schema.External);
_log.Information("Authenticate result: {#authenticateResult}", authenticateResult);
// I get false here and no information on claims etc.
if (!authenticateResult.Succeeded)
{
return Unauthorized();
}
// HttpContext.User does not contain any data either
// code below is not executed
var claimsIdentity = new ClaimsIdentity(Constants.Auth.Schema.Application);
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
_log.Information("Logged in user with following claims: {#Claims}", authenticateResult.Principal.Claims);
await HttpContext.SignInAsync(Constants.Auth.Schema.Application, new ClaimsPrincipal(claimsIdentity));
return LocalRedirect(returnUrl);
}
TLDR: Configuration for SAML in my ASP.NET Core WebApi project looks fine, and I get success response with proper claims which I checked in the logs. I do not know how to extract this data (either return url is wrong or my callback method should work differently). Also, it is puzzling why successfuly redirect from SSO Sign-In page is treated as "unsolicited", maybe this is the problem?
Thanks for any assistance
For anyone who still needs assistance on this issue, I pushed a full working example to github which uses a .Net Core WebAPI for backend and an Angular client using the WebAPI. you can find the example from here:
https://github.com/hmacat/Saml2WebAPIAndAngularSpaExample
As it turned out, the various errors I've been getting were due to my solution being hosted inside docker container. This caused a little malfunction in internal aspnet keychain. More details can be found here (docker is mentioned almost at the end of the article):
https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?tabs=aspnetcore2x&view=aspnetcore-2.2
Long story short, for the code to be working I had to add only these lines:
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("/some/volume/outside/docker")); // it needs to be outside container, even better if it's in redis or other common resource
It fixed everything, which includes:
Sign-in action to external cookie
Unsolicited SSO calls
Exceptions with data protection key chain
So it was very difficult to find, since exceptions thrown by the code didn't point out what's going on (and the unsolicited SSO calls made me think that the SSO provider was wrongly configured). It was only when I disassembled the Saml2 package and tried various code pieces one by one I finally encoutered proper exception (about the key chain) which in turned led me to an article about aspnet data protection.
I provide this answer so that maybe it will help someone, and I added docker tag for proper audience.

How do i solve '"Reference to type 'BaseControlContext" claim.....' for AspNet.Security.OpenIdConnect.Server

I am facing weird issue.
I am reading and creating OpenID Connect server with ASOS this article ASOS - AspNet.Security.OpenIdConnect.Server.
I simply created new sample solution and added new subclass AuthorizationProvider class of OpenIdConnectServerProvider and override the virtual method i.e. ExtractAuthorizationRequest
AuthorizationProvider.cs
public class AuthorizationProvider : OpenIdConnectServerProvider
{
public override Task ExtractAuthorizationRequest(ExtractAuthorizationRequestContext context)
{
// If a request_id parameter can be found in the authorization request,
// restore the complete authorization request stored in the user session.
if (!string.IsNullOrEmpty(context.Request.RequestId))
{
var payload = context.HttpContext.Session.Get(context.Request.RequestId);
if (payload == null)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Invalid request: timeout expired.");
return Task.FromResult(0);
}
// Restore the authorization request parameters from the serialized payload.
using (var reader = new BsonReader(new MemoryStream(payload)))
{
foreach (var parameter in JObject.Load(reader))
{
// Avoid overriding the current request parameters.
if (context.Request.HasParameter(parameter.Key))
{
continue;
}
context.Request.SetParameter(parameter.Key, parameter.Value);
}
}
}
return Task.FromResult(0);
}
}
Issue:
As soon as i add Microsoft.AspNetCore.Identity (2.0.0) NuGet package to my project, context.Reject start giving the following error
"Reference to type 'BaseControlContext" claim it is defined in
Microsoft.AspNetCore.Authentication, but it could not be found.
But as soon as I remove Microsoft.AspNetCore.Identity NuGet dependency, the error goes away and all looks fine.
Note:
I am using VS 2017.
I am using dotnetcore 2.0 SDK.
I created solution using .Net Core 2.0.
Massive changes have been introduced in ASP.NET Core 2.0's authentication stack. The changes are so important that all the authentication middleware written for ASP.NET Core 1.x are not compatible (which includes all the aspnet-contrib projects).
You can read https://github.com/aspnet/Announcements/issues/262 for more information.
The good news is that we have an ASP.NET Core 2.0 RTM-compatible version of ASOS. You can find the 2.0.0-preview1-* bits on the aspnet-contrib MyGet feed (https://www.myget.org/F/aspnet-contrib/api/v3/index.json).

Using MVCMailer with ASP.Net Web Api

I'm using ASP.Net Identity and in my Web Api project in its AccountController I want to send verification email to new users. I have plugged my email service using MVCMailer to the ASP.Net identity.
public class EmailService : IIdentityMessageService
{
private readonly IUserMailer _userMailer;
public EmailService(IUserMailer userMailer)
{
_userMailer = userMailer;
}
public Task SendAsync(IdentityMessage message)
{
_userMailer.DeliverMessage(message.Body);
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
}
#
public class UserMailer : MailerBase, IUserMailer
{
public UserMailer()
{
MasterName = "_Layout";
}
public virtual IMailMessage DeliverMessage(string message)
{
var mailMessage = new MailMessage();
mailMessage.To.Add("hashemp206#yahoo.com");
mailMessage.Subject = "Welcome";
//ViewData = new System.Web.Mvc.ViewDataDictionary(model);
PopulateBody(mailMessage, "Welcome");
mailMessage.Send();
return mailMessage;
}
my custom ASP.Net Identiy is in a seperate project. and as you see above EmailService is dependent on IUserMailer interface. and IUserMailer implementation is in another project MyProject.MVCMailer (this project is an MVC project)
in my dependency resolver in web api I try to resolve this dependency
container.Bind<IUserMailer>().To<UserMailer>().InSingletonScope();
but MVCMailer has a dependency to System.Web.MVC and ninject complain for this reference to initialize USerMailer.
so the problem is here I dont want to add System.Web.MVC to my Web Api project.
how can I use MVCMailer without referencing to System.Web.MVC in my web api project?
I'm a little confused on what you're trying to do but if I'm understanding you correctly you are trying to call your API and then send out an email. Since you are not passing anything from the API into the email (and even if you were) just call your API and return a response object. Once the MVC project recieves the response send the email from the MVC project. Your API should know about objects in your MVC project unless there is a really good reason. Think of them (your MVC and API projects) as two separate entities all together that don't know about each other.

ASP .Net MVC and WCF Identity (Claims) Integration

We're building a platform where the client is an ASP .Net MVC one, using ASP Net Identity 2.0 for authentication and authorization (using Claims), which works great on the web side.
We also have a WCF service which allows CRUD operations on the database (for multiple client applications), which gets requests from this ASP .Net MVC client.
As we want to validate (authenticate & authorize) the user before making specific CRUD actions in the WCF side, we need to get the claims of the user from the client, and perform the validations (preferably in a very clean manner using headers or any binding that WCF will be able to support for this matter).
I've been searching the different forums but with no simple answer\tutorial to this specific scenario. Can anyone assist on this matter?
Thanks,
Nir.
I love this:
in your IEndpointBehavior implementation do this on the client end:
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("token", "http://myurl.com/service/token", _theToken));
return null;
}
then on the service end add this to your ServiceAuthenticationManager
public override ReadOnlyCollection<IAuthorizationPolicy> Authenticate(
ReadOnlyCollection<IAuthorizationPolicy> authPolicy, Uri listenUri, ref Message message)
{
IPrincipal user = new MyUserPrincipal(null);
if(_currentServiceContractType.GetInterfaces()
.Any(x => x == typeof(IMySecuredService)))
{
var tokenPosition = message.Headers.FindHeader("token", "http://myurl.com/service/token");
if (tokenPosition >= 0 && tokenPosition <= 5)
{
var encryptedToken = message.Headers.GetHeader<string>(tokenPosition);
if (!string.IsNullOrWhiteSpace(encryptedToken))
{
var serializedToken = new MyEncryptionUtility().Decrypt(encryptedToken);
var token = MyTokenSerializer.Deserialize(serializedToken);
var expire = new DateTime(token.ValidToTicks);
if (expire > DateTime.Now)
{
user = new MyUserPrincipal(token);
}
}
}
}
message.Properties["Principal"] = user;
Thread.CurrentPrincipal = user;
return authPolicy;
}
This gives you then the ability to use the built in claims or WIF claims authentication. Eitherway, this is very simple. The token is created by the service and sent to the client (web) and stored in the cookie. then when there are any requests, the token is grabbed from a cookie and then sent along to the service, where, inevitably you can start adding permissions service side, versus doing them on the web/mvc side, making a much cleaner code base using everyone's favorite friend, SOA >= :)

How do I authorize access to ServiceStack resources using OAuth2 access tokens via DotNetOpenAuth?

I've created an OAuth2 authorization server using DotNetOpenAuth, which is working fine - I'm using the resource owner password flow, and successfully exchanging user credentials for an access token.
I now want to use that access token to retrieve data from secure endpoints in a ServiceStack API, and I can't work out how to do so. I've examined the Facebook, Google, etc. providers included with ServiceStack but it's not clear whether I should be following the same pattern or not.
What I'm trying to achieve (I think!) is
OAuth client (my app) asks resource owner ('Catherine Smith') for credentials
Client submits request to authorization server, receives an access token
Client requests a secure resource from the resource server (GET /users/csmith/photos)
The access token is included in an HTTP header, e.g. Authorization: Bearer 1234abcd...
The resource server decrypts the access token to verify the identity of the resource owner
The resource server checks that the resource owner has access to the requested resource
The resource server returns the resource to the client
Steps 1 and 2 are working, but I can't work out how to integrate the DotNetOpenAuth resource server code with the ServiceStack authorization framework.
Is there an example somewhere of how I would achieve this? I've found a similar StackOverflow post at How to build secured api using ServiceStack as resource server with OAuth2.0? but it isn't a complete solution and doesn't seem to use the ServiceStack authorization provider model.
EDIT: A little more detail. There's two different web apps in play here. One is the authentication/authorisation server - this doesn't host any customer data (i.e. no data API), but exposes the /oauth/token method that will accept a username/password and return an OAuth2 access token and refresh token, and also provides token-refresh capability. This is built on ASP.NET MVC because it's almost identical to the AuthorizationServer sample included with DotNetOpenAuth. This might be replaced later, but for now it's ASP.NET MVC.
For the actual data API, I'm using ServiceStack because I find it much better than WebAPI or MVC for exposing ReSTful data services.
So in the following example:
the Client is a desktop application running on a user's local machine, the Auth server is ASP.NET MVC + DotNetOpenAuth, and the Resource server is ServiceStack
The particular snippet of DotNetOpenAuth code that's required is:
// scopes is the specific OAuth2 scope associated with the current API call.
var scopes = new string[] { "some_scope", "some_other_scope" }
var analyzer = new StandardAccessTokenAnalyzer(authServerPublicKey, resourceServerPrivateKey);
var resourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(analyzer);
var wrappedRequest = System.Web.HttpRequestWrapper(HttpContext.Current.Request);
var principal = resourceServer.GetPrincipal(wrappedRequest, scopes);
if (principal != null) {
// We've verified that the OAuth2 access token grants this principal
// access to the requested scope.
}
So, assuming I'm on the right track, what I need to do is to run that code somewhere in the ServiceStack request pipeline, to verify that the Authorization header in the API request represents a valid principal who has granted access to the requested scope.
I'm starting to think the most logical place to implement this is in a custom attribute that I use to decorate my ServiceStack service implementations:
using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;
namespace SpotAuth.ResourceServer.Services {
[RequireScope("hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
}
This approach would also allow specifying the scope(s) required for each service method. However, that seems to run rather contrary to the 'pluggable' principle behind OAuth2, and to the extensibility hooks built in to ServiceStack's AuthProvider model.
In other words - I'm worried I'm banging in a nail with a shoe because I can't find a hammer...
OK, after a lot of stepping through the various libraries with a debugger, I think you do it like this: https://github.com/dylanbeattie/OAuthStack
There's two key integration points. First, a custom filter attribute that's used on the server to decorate the resource endpoints that should be secured with OAuth2 authorization:
/// <summary>Restrict this service to clients with a valid OAuth2 access
/// token granting access to the specified scopes.</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
public class RequireOAuth2ScopeAttribute : RequestFilterAttribute {
private readonly string[] oauth2Scopes;
public RequireOAuth2ScopeAttribute(params string[] oauth2Scopes) {
this.oauth2Scopes = oauth2Scopes;
}
public override void Execute(IHttpRequest request, IHttpResponse response, object requestDto) {
try {
var authServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("authServer");
var dataServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("dataServer");
var tokenAnalyzer = new StandardAccessTokenAnalyzer(authServerKeys.PublicSigningKey, dataServerKeys.PrivateEncryptionKey);
var oauth2ResourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(tokenAnalyzer);
var wrappedRequest = new HttpRequestWrapper((HttpRequest)request.OriginalRequest);
HttpContext.Current.User = oauth2ResourceServer.GetPrincipal(wrappedRequest, oauth2Scopes);
} catch (ProtocolFaultResponseException x) {
// see the GitHub project for detailed error-handling code
throw;
}
}
}
Second, this is how you hook into the ServiceStack HTTP client pipeline and use DotNetOpenAuth to add the OAuth2 Authorization: Bearer {key} token to the outgoing request:
// Create the ServiceStack API client and the request DTO
var apiClient = new JsonServiceClient("http://api.mysite.com/");
var apiRequestDto = new Shortlists { Name = "dylan" };
// Wire up the ServiceStack client filter so that DotNetOpenAuth can
// add the authorization header before the request is sent
// to the API server
apiClient.LocalHttpWebRequestFilter = request => {
// This is the magic line that makes all the client-side magic work :)
ClientBase.AuthorizeRequest(request, accessTokenTextBox.Text);
}
// Send the API request and dump the response to our output TextBox
var helloResponseDto = apiClient.Get(apiRequestDto);
Console.WriteLine(helloResponseDto.Result);
Authorized requests will succeed; requests with a missing token, expired token or insufficient scope will raise a WebServiceException
This is still very much proof-of-concept stuff, but seems to work pretty well. I'd welcome feedback from anyone who knows ServiceStack or DotNetOpenAuth better than I do.
Update
On further reflection, your initial thought, to create a RequiredScope attribute would be a cleaner way to go. Adding it to the ServiceStack pipeline is as easy as adding the IHasRequestFilter interface, implementing a custom request filter, as documented here: https://github.com/ServiceStack/ServiceStack/wiki/Filter-attributes
public class RequireScopeAttribute : Attribute, IHasRequestFilter {
public void RequireScope(IHttpRequest req, IHttpResponse res, object requestDto)
{
//This code is executed before the service
//Close the request if user lacks required scope
}
...
}
Then decorate your DTO's or Services as you've outlined:
using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;
namespace SpotAuth.ResourceServer.Services {
[RequireScope("hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
}
Your RequireScope custom filter would be almost identical to ServiceStack's RequiredRoleAttribute implementation., so use it as a starting point to code from.
Alternately, you could map scope to permission. Then decorate your DTO or service accordingly (see SS wiki for details) for example:
[Authenticate]
[RequiredPermission("Hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Normally ServiceStack calls the method bool HasPermission(string permission) in IAuthSession. This method checks if the list List Permissions in IAuthSession contains the required permission, so, in a custom IAuthSession you could override HasPermission and put your OAuth2 scopes checking there.

Resources