Service Stack authentication - servicestack

? - Is it possible to have multiple authentication providers within the same MVC 4 hosted service stack web services, we will have multiple endpoints utilizing internal and external services that require differing levels/types of authentication.
I need initially to implement a couple of custom providers to suit our our needs so that depending on the URL a different authentication provider is utilized, so far I have only found examples of providing a single custom authentication provider.

Yes. You can use multiple providers. Then you could have different roles for different resources (urls) to manage your internal vs external levels.
Take a look at the https://github.com/ServiceStack/SocialBootstrapApi example project. This example has a lot of different authentication providers. Each auth provider resolves to the path /auth/{provider} where provider is resolved using the IAuthProvider.Provider property of your custom providers and the build in providers.
You will need to register each auth provider you want to use.
//Register all Authentication methods you want to enable for this web app.
Plugins.Add(new AuthFeature(
() => new CustomUserSession(), //Use your own typed Custom UserSession type
new IAuthProvider[] {
new CredentialsAuthProvider(),
new TwitterAuthProvider(appSettings),
new FacebookAuthProvider(appSettings),
new DigestAuthProvider(appSettings),
new BasicAuthProvider(),
new GoogleOpenIdOAuthProvider(appSettings),
new YahooOpenIdOAuthProvider(appSettings),
new OpenIdOAuthProvider(appSettings),
}));
Then you can login by hitting the different urls like
/auth/facebook
/auth/twitter

Related

Share ASP.NET Core data protection between applications without breaking existing cookies and tokens

We are using ASP.NET Core Data Protection in combination with ASP.NET Identity with Cookie Authentication in an ASP.NET Core web application. We also send Reset-Password links using ASP.NET Core Identity which uses the data protection keys for that. We persist the data protection keys in our database using entity framework. By storing the keys in the database, we don't have any issues when swapping deployment slots in Azure.
services
.AddDataProtection()
.PersistKeysToDbContext<KeysContext>();
This all works as expected and we are running in production for several years already.
We now have a new feature, where the user can delay the sending of invitation links for new users. These generated invitation links use the ResetPassword token provider from ASP.NET Identity. We are using an Azure Function for that, where the invitation links are generated and sent in the Azure Function at a later point in time.
var token = await this.userManager.GeneratePasswordResetTokenAsync(user);
The problem now is, that the Azure Function needs to use the same data protection keys as the web application, since the generated ResetPassword tokens are later "consumed" and verified in the web application. This can be done using the ApplicationDescriminator when configuring the data protection. Every application (i.e our web application and our Azure Function) need to use the same ApplicationDescriminator:
services
.AddDataProtection(o => o.ApplicationDiscriminator = "Our-Application-Name")
.PersistKeysToDbContext<KeysContext>();
But when we now set the ApplicationDescriminator in our existing and running web application initially to "Our-Application-Name", all our already sent tokens (Invitations, Reset Password, Change Email, ...) will get invalid and also our ASP.NET Core Identity Cookie will get invalid and all users will get logged out.
Is there any way of telling the Azure Function to use the same data protection keys as the web application without changing or breaking the existing tokens in the web application?
We found a pretty hacky solution to not break the existing tokens and cookies in the web application: Instead of specifying the ApplicationDiscriminator in the web application and in the Azure Function explicitly, we specify the ApplicationDescriminator only in the Azure Function and set it to "D:\home\site\wwwroot".
This value is the default value in the web application when not specifying any value, since the default implementation in the ASP.NET Core data protection uses the HostingApplicationDiscriminator which uses the IHostEnvironment.ContentRootPath property. For an Azure deployment, this ContentRootPath is set to "D:\home\site\wwwroot" by default.
We are not very happy with this approach, since this seems like a hack, but it's still better than breaking all tokens and cookies by specifying the ApplicationDescriminator explicitly.

Allow access to single API without Authorization?

I'm sure I'm probably missing something fairly basic, but is there a way to expose ApiController to be accessed without Authorization? Removing [Authorize] or adding [AllowAnonymous] doesn't seem to help.
namespace Backend.Controllers
{
[MobileAppController, Authorize]
public class RebuildLocalDatabaseCheckController : ApiController
{
[HttpGet, Route("api/LastLocalDatabaseRebuildRequest")]
[AllowAnonymous]
public JToken Get()
{
return JToken.FromObject(DateTimeOffset.Now);
}
}
}
This is for an Azure Mobile App that I've setup for a UWP app. I do want everything else to require authorization, and that part is working fine. I just want to expose this part without needing login from the user.
I have checked your code on my local side and it could work as expected.
For anonymous access, you need to remove the Authorize from your RebuildLocalDatabaseCheckController class for all actions or specify the AllowAnonymous for your specified action. For more details about custom API in azure mobile apps project, you could refer to adrian hall's book here.
Additionally, if you app has been deployed to azure mobile app, for allowing anonymous against your custom API, you need to disable App Service Authentication or enable it with Allow Anonymous requests (no action) as follows:
Additionally, here is a blog about Architecture of Azure App Service Authentication / Authorization, you could refer to it for better understanding of app service authentication / authorization.

How to secure custom parts of Identity Server

I want to extend IdentityServer 3 with a 'admin' part where users can manage things like users, clients, etc. This part should be secured by the same ID server implementation (same app in IIS). Do I have to build a separate app or can I extend the same ID server solution? How do I configure the OWIN start up then? When I have
app.Map("/Identity"....)
how do I add:
app.UseOpenIdConnectAuthentication
This results in an 'external' login provider, but that is not what I want. I also tried to add:
app.Map("/admin", config => config.UseOpenIdConnectAuthentiaction())
But that does not work as well, so:
How to have ID server and a client combined in one Solution?
Please help.
Have a look at IdentityManager provided by developers of IdentityServer. This will get you up & running very quickly.
Security Model
The security model can be configured to only allow users running on the same machine or can be configured to use any Katana based authentication middleware to authenticate users.
Hosting Options
IdentityManager is hosted as OWIN middleware. It can be configured with the UseIdentityManager extension method for Katana
This is how you "Get started"

Adding additional claims to authenticated users in Azure Mobile App

I am in the process of learning the Azure Mobile App possibilities in Azure App Services and have a couple of questions in regards to authentication (and authorization). I use the Azure Mobile App Quickstart to learn the available functionality.
First question:
Do I understand it correctly that I can't implement custom authentication using the Microsoft.Azure.Mobile.Server.Login package as described in the section "How to: Use custom authentication for your application" of the article Work with the .NET backend server SDK for Azure Mobile Apps as I want to use the built-in Azure authentication providers (for authentication with Facebook, Google, etc.)?
I prefer configuration and using the built-in functionality over custom development when it comes to security ;-)
Second question:
I am currently trying to add some custom claims (e.g. a role claim read from an Azure SQL database based on the user's SID) to the users that successfully registered and authenticated in my Azure Mobile App.
What is the best way to accomplish that?
My idea was to add the claims in the Initialize method of the TableController like following and then check for the role using the [Authorize(Roles = "Test")] attribute.
Is there any reason why not to do it this way?
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
myapptestContext context = new myapptestContext();
DomainManager = new EntityDomainManager<TodoItem>(context, Request);
var claimsPrincipal = this.User as ClaimsPrincipal;
ClaimsIdentity ci = new ClaimsIdentity();
string role = "Test"; // get role from Azure SQL database based on user's SID
ci.AddClaim(new Claim(ClaimTypes.Role, role));
claimsPrincipal.AddIdentity(ci);
}
If you intend to use the built-in authentication providers, then you don't need to use the Microsoft.Azure.Mobile.Server.Login. The built-in providers (AAD, Facebook, Google, Twitter, MSA) will take care of all the details for you.
As far as custom claims go, I don't have any specific guidance. You could put it in the controller initialization, or you could add a piece of custom middleware which injects it, or even a callback in Global.asax. Whatever works best for you and your app.
There is a good article describing how to add custom claims to the Identity by using a custom token handler. In the handler the roles for the user are added as claims.
See here: https://blogs.perficient.com/microsoft/2016/05/how-to-add-custom-claims-to-azure-mobile-app-authentication/

how to build domain based Multi-tenant SaaS solution built using OWIN claims based authentication

I'm attempting to create a SaaS solution and i want each tenant of my application to have its own Access Control Service (ACS) server or identity server configuration, I plan on determining the current tenant by the subdomain currently being visited
eg tenant1.Myapp.com, tenant2.Myapp.com
I want to be able to have different tenant config for each tenant and i don't want each tenant to be aware of other tenants currently registered.
I will have custom branding for each tenant
public partial class Startup
{
private void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions() { LoginPath = new Microsoft.Owin.PathString("/Authentication/SignIn") });
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
MetadataAddress = "https://XXXXX.accesscontrol.windows.net/federationmetadata/2007-06/federationmetadata.xml",
Wtrealm = "urn:operations.web"
}
);
}
}
The above code works for a single tenant application but there is no way of knowing in advance which of the ACS identity providers to use for authentication.
Some of the tenants want to be able to login users using Google, Microsoft Account, Facebook
I see you are using ACS to enable multiple IPs in your application. That's a great first step though the ACS functionality is under process to be folded into the AAD.
In your case you need to download the json feed of all your IPs from the ACS and then filter it based on the information in the request headers that contains the subdomain name.
Mu blog post here takes you all the way to this point and then you need to go the rest of the way yourself.
http://magnusmartensson.com/mvc-application-using-owin-to-achieve-federated-authentication
A typical ACS json feed address looks something like this:
https://{mytenant}.accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=http%3a%2f%2flocalhost%3a7664%2f&reply_to=http%3a%2f%2flocalhost%3a7664%2f&context=&request_id=&version=1.0&callback=
When you get on that feed you will receive an array of your configured IPs in the ACS:
[
{"Name":"Windows Liveā„¢ ID","LoginUrl":"https://login.live.com/login.srf?wa=wsignin1.0&wtrealm=https%3a%2f%2faccesscontrol.windows.net%2f&wreply=https%3a%2f%2fSOMENAME.accesscontrol.windows.net%2fv2%2fwsfederation&wp=MBI_FED_SSL&wctx=cHI9d3NmZWRlcmF0aW9uJnJtPWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJmJnJ5PWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJm0","LogoutUrl":"https://login.live.com/login.srf?wa=wsignout1.0","ImageUrl":"","EmailAddressSuffixes":[]},
{"Name":"Google","LoginUrl":"https://www.google.com/accounts/o8/ud?openid.ns=http%3a%2f%2fspecs.openid.net%2fauth%2f2.0&openid.mode=checkid_setup&openid.claimed_id=http%3a%2f%2fspecs.openid.net%2fauth%2f2.0%2fidentifier_select&openid.identity=http%3a%2f%2fspecs.openid.net%2fauth%2f2.0%2fidentifier_select&openid.realm=https%3a%2f%2fSOMENAME.accesscontrol.windows.net%3a443%2fv2%2fopenid&openid.return_to=https%3a%2f%2fSOMENAME.accesscontrol.windows.net%3a443%2fv2%2fopenid%3fcontext%3dcHI9d3NmZWRlcmF0aW9uJnJtPWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJmJnJ5PWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJmJnByb3ZpZGVyPUdvb2dsZQ2&openid.ns.ax=http%3a%2f%2fopenid.net%2fsrv%2fax%2f1.0&openid.ax.mode=fetch_request&openid.ax.required=email%2cfullname%2cfirstname%2clastname&openid.ax.type.email=http%3a%2f%2faxschema.org%2fcontact%2femail&openid.ax.type.fullname=http%3a%2f%2faxschema.org%2fnamePerson&openid.ax.type.firstname=http%3a%2f%2faxschema.org%2fnamePerson%2ffirst&openid.ax.type.lastname=http%3a%2f%2faxschema.org%2fnamePerson%2flast","LogoutUrl":"https://www.google.com/accounts/Logout","ImageUrl":"","EmailAddressSuffixes":[]},
{"Name":"SOMENAME","LoginUrl":"https://login.windows.net/3c54a3bf-d2b0-4dae-bdd6-61d05145c8be/wsfed?wa=wsignin1.0&wtrealm=https%3a%2f%2fviewmybox.accesscontrol.windows.net%2f&wreply=https%3a%2f%2fviewmybox.accesscontrol.windows.net%2fv2%2fwsfederation&wctx=cHI9d3NmZWRlcmF0aW9uJnJtPWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJmJnJ5PWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJm0","LogoutUrl":"https://login.windows.net/3c54a3bf-d2b0-4dae-bdd6-61d05145c8be/wsfed?wa=wsignout1.0","ImageUrl":"","EmailAddressSuffixes":[]},
{"Name":"Facebook","LoginUrl":"https://www.facebook.com/dialog/oauth?client_id=284082335104349&redirect_uri=https%3a%2f%2fviewmybox.accesscontrol.windows.net%2fv2%2ffacebook%3fcx%3dcHI9d3NmZWRlcmF0aW9uJnJtPWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJmJnJ5PWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJmJmlwPUZhY2Vib29rLTI4NDA4MjMzNTEwNDM0OQ2&scope=email","LogoutUrl":"https://www.facebook.com/logout.php?access_token={0}&next={1}","ImageUrl":"","EmailAddressSuffixes":[]},
{"Name":"Martensson Consulting","LoginUrl":"https://login.windows.net/8e717ff8-8b9b-4185-b61c-aea1fd724035/wsfed?wa=wsignin1.0&wtrealm=https%3a%2f%2fviewmybox.accesscontrol.windows.net%2f&wreply=https%3a%2f%2fviewmybox.accesscontrol.windows.net%2fv2%2fwsfederation&wctx=cHI9d3NmZWRlcmF0aW9uJnJtPWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJmJnJ5PWh0dHAlM2ElMmYlMmZsb2NhbGhvc3QlM2E3NjY0JTJm0","LogoutUrl":"https://login.windows.net/8e717ff8-8b9b-4185-b61c-aea1fd724035/wsfed?wa=wsignout1.0","ImageUrl":"","EmailAddressSuffixes":[]}
]
Naturally you only want to use one of a couple of these IPs for each of your subdomains/tenants/customers. What you need to do is server side filter this feed and provide your login page with only the ones your customer accepts. If it's only one you might even want to redirect from your application straight to that one single option for customer sign in.
This requires you download the login sample page from the ACS and build your own custom version.
Hope this helps!

Resources