I have a couple of self-hosted windows services running with ServiceStack. These services are used by a bunch of WPF and WinForms client applications.
I have written my own CredentialsAuthProvider. My first implementation of the user database was on MSSQL server using NHibernate. Now since the system is growing I reorganize things a bit. I have created a central 'infrastructue' service which uses Redis as data store and is responsible for account management, central configuration and preferences management. Later it will also contain central logging and RedisMQ. All accounts, roles etc are now stored there (instead of MSSQL). Account migration was successfuly and authentication works fine so far.
Now I have the problem, that clients and servers need to get and set their configurations / preferences. This means that my servers are also clients since they not only serve client requests for their specific business domain but itself need to call the 'infrastructure' server to load / update its own configuration and later log entries and messages.
To authenticate such requests I thought an API key is a good way to go. These requests are not user related and therefore do not need a gateway functionality, they simply need some communication with the central infrastructure server. So I was reading the ServiceStack docs about the API Key Provider, but unfortunately for me a lot remains unclear.
Here first some relevant code from my 'infrastructure' server's Configure method:
private PooledRedisClientManager RedisBusinessPool { get; set; }
//...
container.Register<IRedisClientsManager>(c => new PooledRedisClientManager(connStrBus));
container.Register(c => new AppUserRepository(RedisBusinessPool));
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new BediCredentialsAuthProvider(),
}
));
// For the API keys I tried:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new ApiKeyAuthProvider(AppSettings)
{
KeyTypes = new []{"secret", "publishable"},
},
}
));
Since I enabled the API Key plugin I get an error on the client when I try to login:
ERROR; AccountManagerWinDesktop; [LoginViewModel+<Login>d__50.MoveNext]; - <username> failed to login to server <myInfrastructureServer>. Exception: 404 NotFound
Code: NotFound, Message: No configuration was added for OAuth provider 'credentials'
Does this mean, that I have to implement my own ApiKeyProvider to cooperate with my implementation of the CredentialAuthProvider? If so, what do I need to add?
In my CredentialAuthProvider implementation I have overwritten Logout, Authenticate, OnAuthenticated and TryAuthenticate. A WPF client offers a UI to store users and roles. They are stored on the Redis database including hashed passwords etc. In my TryAuthenticate implementation I simply have:
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
AppUser user = null;
try
{
//the repository handles all Redis database access
var userRepo = HostContext.TryResolve<AppUserRepository>();
user = userRepo.GetAppUser(userName);
if (user == null)
throw HttpError.NotFound("User '{0}' not found. Please try again.".Fmt(userName));
authService.Request.Items.Add("AppUser", user);
var pwdMgr = new PwdManager();
var hpwd = pwdMgr.GetHashedPassword(password, user.Salt);
if (hpwd == user.Password)
{
//do stuff
}
else
{
// do other stuff
}
return hpwd == user.Password;
}
catch (Exception ex)
{
Log.Error($"Error retrieving user {user} to authenticate. Error: {ex}");
throw;
}
}
What I do not understand right now - Questions:
How are API keys related to my own implementation of CredentialsAuthProvider?
How can I issue API keys to an application server? (I read that ServiceStack creates keys automatically when a user is created, but I do not need this in my scenario)
Do I also have to implement my own ApiKeyAuthProvidersimilar to the CredentialsAuthProvider I have overwritten? If so, is there a sample somewhere?
Is there any object / data model for API keys?
Do I need to implement something like the TryAuthenticate method above to verify my API Keys?
You should only ever register 1 of any Plugin type, so change your AuthFeature plugin to register all Auth Providers you want to enable, e.g:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new BediCredentialsAuthProvider(),
new ApiKeyAuthProvider(AppSettings)
{
KeyTypes = new []{"secret", "publishable"},
},
}
));
How are API keys related to my own implementation of CredentialsAuthProvider?
An API Key is assigned to a User, i.e. when a request is received with an API Key, they're authenticated as the user the API Key is assigned to. API Keys are created for each new user that's registered, the above configuration creates a secret and publishable key for a new register created with the /register service.
API Keys requires using a User Auth Repository
Your users need to be persisted in an AuthRepository in order to use the API Key AuthProvider. The list of support Auth Repositories are listed on in the documentation. Although you can use your own custom User Auth Repository if it implements IUserAuthRepository and IManableApiKeys interfaces.
How can I issue API keys to an application server? (I read that ServiceStack creates keys automatically when a user is created, but I do not need this in my scenario)
An API Key is assigned to a User - all of ServiceStack AuthProviders revolves around Authenticating Users. One idea is to create a "User" to represent that App Server. You can use the IManageApiKeys API to create your own API Keys, there's an example of using this in the code-snippet for creating API Keys for existing Users.
Do I also have to implement my own ApiKeyAuthProvider similar to the CredentialsAuthProvider I have overwritten? If so, is there a sample somewhere?
You wont need to implement anything to use the existing ApiKeyAuthProvider but if it doesn't do what you need you can take ApiKeyAuthProvider.cs and create a customized version that does what you need.
Is there any object / data model for API keys?
The ApiKey class is the model that contains the API Key itself, which is persisted in all supported Auth Repositories.
Do I need to implement something like the TryAuthenticate method above to verify my API Keys?
No.
Related
I will buy anyone a beer who can solve my problem!
As a piece of work I need to update our Identity Server to use an implicit login flow, it was currently using Bearer Token access only. As a part of our architecture we are using Multi-tenancy.
For security reaosns we need to check the tenant header to verify that the user is not impersonating another tenant. To do this from a client perspective we use a custom IProfileService. This gets triggered in the middleware of Identity Server, meaning all is good!
However if I was a user and I wanted to use some form of functionality on Identity Server itself and not an actual client of it, then IProfileService will not be triggered. An example of this would be to revoke access to clients, or even log out.
The GetProfileDataAsync Method on IProfileService is Invoked when the client request additional claims for the user.
germansak on Github Issue here had a similar issue and it was never quite answered (https://github.com/IdentityServer/IdentityServer4/issues/1643)
Leading to my question, how has anyone been able to verify a Tenant on Identity Server itself when they are not going through a Client, but instead Identity Server. If I can't trigger IProfileService I feel as if I'm beat!
Both logout and grants functionality is not part of the identity server 4 core package and they are simply implemented as ASP.NET Core razor views outside of the oauth2 flows.
There are few ways to validate headers therefore, I guess the easiest in my opinion would be to add another middleware.
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<TenantHeaderValidationMiddleware>();
...Your other config
}
public class TenantHeaderValidationMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
string tenantId = Request.Headers["YourTenantIdHeader"];
//do the checking
...Validation logic
//pass request further if correct
await _next(context);
}
}
I've almost configured my OpenId owin authentication/authorization in Azure Active Directory. My configuration is the following:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
CookieName = "AppServiceAuthSession"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ClientId,
Authority = _authority,
PostLogoutRedirectUri = PostLogoutRedirectUri,
RedirectUri = PostLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
},
AuthorizationCodeReceived = async context =>
{
var id = new ClaimsIdentity(context.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(context.AuthenticationTicket.Identity.Claims);
var appToken = "MyToken";
id.AddClaim(new Claim("MyTokenKey", appToken));
context.AuthenticationTicket = new AuthenticationTicket
(
new ClaimsIdentity(id.Claims, context.AuthenticationTicket.Identity.AuthenticationType),
context.AuthenticationTicket.Properties
);
}
},
});
But I want to add one more application token (not user token) to claims list to be able to have ability to use this token in any place on my site. Also it's good point for me that I don't need to get this token from my external token provider more then one time per an authentication session.
But place, where I'm going to add my logic (AuthorizationCodeReceived as well as other methods from OpenIdConnectAuthenticationNotifications) is called only when I use my local IIS(run locally), when I try to use azure IIS, this method has not been called at all. In this case my User is authenticated anyway, but this method and the similar methods from OpenIdConnectAuthenticationNotifications(except RedirectToIdentityProvider) are not fired.
I've downloaded the git source code of Katana project and referenced this project to my instead of the official nuget packages to debug its and as I think currently, I've found the reason why it happens. The AuthorizationCodeReceived "event" method is called from OpenIdConnectAuthenticationHandler class in AuthenticateCoreAsync method. But also, the calling of this method is required that the below checking must give the true result:
if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match.
&& Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)
&& Request.Body.CanRead)
{
//some necessary preparation to call `AuthorizationCodeReceived` event method
}
As we can see, this checking allows only POST requests and I see these POST requests when I run app in local IIS, but I cannot see these POST requests when I deploy my application in azure portal (I've debugged both of options : on local IIS and in azure portal).
As summary from the above, this is the only one difference between these runnings. (Azure IIS doesn't send POST request at all by some reason).Any other methods in Katana project (which I checked) are called in the same way.
Could anybody help with it?
PS Note, I check any changes only after clearing of browser data (cache/history and so on).
The answer is the following:
The authorization in azure portal should be configured as shown above. In case if you chose LogIn with Azure Active Directory, then app services auth takes place outside of your app, and the custom authorization is not triggered.
I've successfully setup a custom authentication process with Azure Mobile Services and my Windows Phone 8.1 app (following the guide here)
I'm now creating an MVC5 single page application (SPA) to manage the admin side of things for the system. I'm relatively new to MVC5 and just need a little help to get started with performing a login just like in my phone app?
Currently my phone app performs a login by
App.MobileService.CurrentUser = await AuthenticateAsync(this.textBox_email.Text, textBox_password.Password);
which does
private async Task<MobileServiceUser> AuthenticateAsync(string username, string password)
{
// Call the CustomLogin API and set the returned MobileServiceUser
// as the current user.
var user = await App.MobileService
.InvokeApiAsync<LoginRequest, MobileServiceUser>(
"CustomLogin", new LoginRequest()
{
UserName = username,
Password = password
});
return user;
}
this all works well so I guess the question is how do I do make a call to my customer authentication API in the same way in MVC5 and set the user context if successful?
Startup.Auth.cs:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication();
}
Let me know if I'm missing any info or detail.
Thanks!
Unfortunately this is not easy to do in Mobile Services. While you could achieve login using the Mobile Services HTML/JS SDK (served in an MVC view), this will not set the user context.
Because of Mobile Services incompatibility with MVC (addressed in the new Mobile Apps product), you won't be able to rely on that SDK. Unfortunately that means writing custom middleware/filters.
The easiest solution is probably to package your username/password validation and storage logic into code that can be shared by your Mobile Services project and your MVC project. The MVC project would need to take the validated user and issue a session cookie which is then read by a custom middleware or filter.
Writing an AuthorizationFilter implementation will be much easier than an OWIN middleware, so I would recommend that approach. Check if the cookie is present and valid, and if so set the user context.
I am developing a mobile app in which I need to authenticate a user against Azure AD. Basically the user will be prompted their organisational email and password, which the mobile phone app sends to the backend server which will authenticate.
I have the 'public-client-app-sample' of 'azure-activedirectory-library-for-java' working, and can authenticate against 'graph.windows.net':
private static AuthenticationResult getAccessTokenFromUserCredentials(
String username, String password) throws Exception {
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(AUTHORITY, false, service);
Future<AuthenticationResult> future = context.acquireToken(
"https://graph.windows.net", CLIENT_ID, username, password,
null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException(
"authentication result was null");
}
return result;
}
However, this does not return any userInfo (is null), and I can't figure out at this moment how to query to get a list with groups the user belongs to?
Do I just do manual lookups using the API using the tokens obtained from Adal4j, or is there a provided function within the library?
I am only starting with Azure, so it might be obvious, I might just be looking in the wrong places. I tried e.g. 'https://graph.windows.net/xxx.com/groups?api-version=1.5' but get 'Resource 'https://graph.windows.net/xxx.com/groups?api-version=1.5' is not registered for the account.'
First, you're absolutely right, adal4j was failing to return UserInfo. The reason for this was that the token request did not include the scope=openid parameter, which is required if the caller wants an id_token in the response. I opened an issue, and it has already been resolved. So, an updated copy of adal4j will fix your first issue of not getting UserInfo.
Now, regarding group membership for the current user: Normally, I would recommend that you simply configure you application to return the groups claim. This can be done very easily by changing the application manifest (downloaded and uploaded via the Azure portal, under the Application's configuration page) to include:
"groupMembershipClaims": "All",
Unfortunately, adal4j does not yet include the groups claim in the result of getUserInfo(), so that probably won't work much for you (issue opened, it really depends on how quickly it gets implemented, or if you want to implement youself).
Regardless, because it is possible for there to be too many groups to include in the token (indicated by , your application should always be able to use the AAD Graph API to retrieve the full set of groups the user is a member of.
And that brings me to the last point: adal4j does not implement a client of the Azure AD Graph API. So yes, you would have to implement that yourself (or perhaps use/modify an existing OData client).
(You can read more about Group Claims in this Azure AD blog post.)
I'm creating a new web application that will be written using MVC 5 and Entity Framework Database First Approach. I would also like to use ASP.Net Identity to look after membership, authentication, authorisation etc.
I've read a good bit about the ASP.Net Identity on the web and how it works, however, I am still learning about this topic.
When I created my MVC 5 application in Visual Studio 2013 and looked at the Account Controller my first instinct was that I didn't like what I saw, i.e., a DbContext was being referenced named 'ApplicationDbContext'. The reason I didn't like this was because I prefer to keep my DbContext in the appropriate project within my solution, i.e., in my Model layer which adheres to the separation of concerns logic.
Also, the out of the box MVC 5 project uses Entity Framework Code First to create a default database and tables to store the Users, Roles etc.
Because I must use an existing database with an existing User table, this approach does not suit my needs.
I still want to use the latest ASP.Net Identity for my application as it looks to have many benefits, therefore, I found this article which stripped back alot of the Entity Framework code but still got OWIN powered authentication into an ASP.NET MVC.
http://www.khalidabuhakmeh.com/asp-net-mvc-5-authentication-breakdown-part-deux
Using the tutorial above, here is the HttpPost Login method for my Account Controller
[HttpPost]
[AllowAnonymous]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
//Calling my own custom Account Service which validates users login details
var user = _AccountService.VerifyPassword(model.UserName, model.Password, false);
if (user)
{
var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.UserName), }, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);
//ToDo: Manually adding Role, but will pull from db later
identity.AddClaim(new Claim(ClaimTypes.Role, "guest"));
AuthenticationManager.SignIn(new AuthenticationProperties
{
IsPersistent = model.RememberMe
}, identity);
return RedirectToAction("Index", "MyDashboard");
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
return View(model);
}
In my previous MVC applications I usually rolled my own custom membership and when a User logged into the site and was authenticated, I would have stored the any additional user details such as userID, DOB etc in the UserData string of the FormsAuthenticationTicket.
As the code above does not use FormsAuthentication, instead it uses OWIN CookieAuthentication, I am not sure how to store this additional user data.
Therefore, I have a few questions about the problems I am experiencing.
How do I store the userID or any other additional piece of user data (DOB etc) the way I used to in FormsAuthentication? Is this done by adding a Claim to the identity?
Does the method of using ASP.Net Identity/ OWIN above seem correct considering I am using Entity Framework Database First with an existing database?
Should I be using the out of the box code that is used in the Account Controller, i.e., UserManager, ApplicationUser, ApplicationDbContext etc and hooking this up to work with my existing database?
I apologise if my question is confusing, I suppose I'm just a little unsure of what approach I should be using whilst attempting to use ASP.Net Identity in my latest project.
Any feedback would be greatly appreciated.
Thanks.
1) The new Katana Cookie middleware supports claims. This is what makes this better than forms auth cookie; claims model any key/value pair and those can be stored in the authentication cookie. See this post for more details:
http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
2 & 3) As far as your storage for identity data, if you need to work with an existing table then you might not be able to use Microsoft's EF provided classes. Instead you'd be left on your own to implement IUserStore and all the other store interfaces your app needs. I'm not certain it's worth changing what you're already using to store the user data.
Keep in mind that the OWIN/Katana part is separate from the identity storage.
Here is the solution
To speed things up you can add sample app to your project and start by modifying the sample app, Samples app includes confirmation email, password recovery, roles admin and user role management etc. NuGet package is at:
Install-Package Microsoft.AspNet.Identity.Samples -Pre
See full details on sample app here: ASP.NET Identity 2.0: Customizing Users and Roles
Controll access to controller or Action by using below attributes
[Authorize] //Anyone with authorization
[Authorize(Roles="Administrator")] //Admin role only
Check if user is in role by
HttpContext.User.IsInRole("Administrator")
UserManager.IsInRole(userID, "Administrator")
Get profile data by
// Create manager
var manager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext()))
// Find user
var user = manager.FindById(User.Identity.GetUserId());
var profileProperty_1 = user.profileProperty_1