I have had some problems with authentication in ASP.NET. I'm not used most of the built in authentication in .NET.
I gotten some complaints from users using Internet Explorer (any version - may affect other browsers as well) that the login process proceeds but when redirected they aren't authenticated and are bounced back to loginpage (pages that require authentication check if logged in and if not redirect back to loginpage). Can this be a cookie problem?
Do I need to check if cookies are enabled by the user?
What's the best way to build authentication if you have a custom member table and don't want to use ASP.NET login controls?
Here my current code:
using System;
using System.Linq;
using MyCompany;
using System.Web;
using System.Web.Security;
using MyCompany.DAL;
using MyCompany.Globalization;
using MyCompany.DAL.Logs;
using MyCompany.Logging;
namespace MyCompany
{
public class Auth
{
public class AuthException : Exception
{
public int StatusCode = 0;
public AuthException(string message, int statusCode) : base(message) { StatusCode = statusCode; }
}
public class EmptyEmailException : AuthException
{
public EmptyEmailException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMPTY_EMAIL, 6) { }
}
public class EmptyPasswordException : AuthException
{
public EmptyPasswordException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMPTY_PASSWORD, 7) { }
}
public class WrongEmailException : AuthException
{
public WrongEmailException() : base(Language.RES_ERROR_LOGIN_CLIENT_WRONG_EMAIL, 2) { }
}
public class WrongPasswordException : AuthException
{
public WrongPasswordException() : base(Language.RES_ERROR_LOGIN_CLIENT_WRONG_PASSWORD, 3) { }
}
public class InactiveAccountException : AuthException
{
public InactiveAccountException() : base(Language.RES_ERROR_LOGIN_CLIENT_INACTIVE_ACCOUNT, 5) { }
}
public class EmailNotValidatedException : AuthException
{
public EmailNotValidatedException() : base(Language.RES_ERROR_LOGIN_CLIENT_EMAIL_NOT_VALIDATED, 4) { }
}
private readonly string CLIENT_KEY = "9A751E0D-816F-4A92-9185-559D38661F77";
private readonly string CLIENT_USER_KEY = "0CE2F700-1375-4B0F-8400-06A01CED2658";
public Client Client
{
get
{
if(!IsAuthenticated) return null;
if(HttpContext.Current.Items[CLIENT_KEY]==null)
{
HttpContext.Current.Items[CLIENT_KEY] = ClientMethods.Get<Client>((Guid)ClientId);
}
return (Client)HttpContext.Current.Items[CLIENT_KEY];
}
}
public ClientUser ClientUser
{
get
{
if (!IsAuthenticated) return null;
if (HttpContext.Current.Items[CLIENT_USER_KEY] == null)
{
HttpContext.Current.Items[CLIENT_USER_KEY] = ClientUserMethods.GetByClientId((Guid)ClientId);
}
return (ClientUser)HttpContext.Current.Items[CLIENT_USER_KEY];
}
}
public Boolean IsAuthenticated { get; set; }
public Guid? ClientId {
get
{
if (!IsAuthenticated) return null;
return (Guid)HttpContext.Current.Session["ClientId"];
}
}
public Guid? ClientUserId {
get {
if (!IsAuthenticated) return null;
return ClientUser.Id;
}
}
public int ClientTypeId {
get {
if (!IsAuthenticated) return 0;
return Client.ClientTypeId;
}
}
public Auth()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
IsAuthenticated = true;
}
}
public void RequireClientOfType(params int[] types)
{
if (!(IsAuthenticated && types.Contains(ClientTypeId)))
{
HttpContext.Current.Response.Redirect((new UrlFactory(false)).GetHomeUrl(), true);
}
}
public void Logout()
{
Logout(true);
}
public void Logout(Boolean redirect)
{
FormsAuthentication.SignOut();
IsAuthenticated = false;
HttpContext.Current.Session["ClientId"] = null;
HttpContext.Current.Items[CLIENT_KEY] = null;
HttpContext.Current.Items[CLIENT_USER_KEY] = null;
if(redirect) HttpContext.Current.Response.Redirect((new UrlFactory(false)).GetHomeUrl(), true);
}
public void Login(string email, string password, bool autoLogin)
{
Logout(false);
email = email.Trim().ToLower();
password = password.Trim();
int status = 1;
LoginAttemptLog log = new LoginAttemptLog { AutoLogin = autoLogin, Email = email, Password = password };
try
{
if (string.IsNullOrEmpty(email)) throw new EmptyEmailException();
if (string.IsNullOrEmpty(password)) throw new EmptyPasswordException();
ClientUser clientUser = ClientUserMethods.GetByEmailExcludingProspects(email);
if (clientUser == null) throw new WrongEmailException();
if (!clientUser.Password.Equals(password)) throw new WrongPasswordException();
Client client = clientUser.Client;
if (!(bool)client.PreRegCheck) throw new EmailNotValidatedException();
if (!(bool)client.Active || client.DeleteFlag.Equals("y")) throw new InactiveAccountException();
FormsAuthentication.SetAuthCookie(client.Id.ToString(), true);
HttpContext.Current.Session["ClientId"] = client.Id;
log.KeyId = client.Id;
log.KeyEntityId = ClientMethods.GetEntityId(client.ClientTypeId);
}
catch (AuthException ax)
{
status = ax.StatusCode;
log.Success = status == 1;
log.Status = status;
}
finally
{
LogRecorder.Record(log);
}
}
}
}
A classic case of over-engineered Authentication mechanism and on top of it the design is bad.
Exceptions should be out of Auth class but reside in same namespace. Can you imagine how .Net framework would look if Microsoft had created exceptions like this. Always keep it simple, stupid (KISS). It seems you need modular code. Try to be simple yet modular.
Your authentication Client-Keys are static magic-values and you're shipping them with your assemblies. Use SecureString instead of readonly string. Anybody can get hold of it using Reflector. How do you sustain change ad security?
Your code directly refers Current HttpContext object when in fact you could have passed the reference of current context object in client-code that will use this.
RequireClientOfType is int[] - why in the world you want to do this ? I believe it could have been an enum or an immutable struct if at all ever needed.
You are already using FormsAuthentication in your Login() and Logout() which is sufficient to replace your entire Auth. Why do you want to re-invent the wheel if ultimately you are going to use FormsAuthnetication to take care of Auth.
And yes if you cannot revise this design please use FxCop/StyleCop at least to avoid spaghetti-code.
Also you could make class Auth as static and expose functionalities like FormsAuthentication does. And also rename it from Auth to Authentication.
This is a prime candidate for http://thedailywtf.com/
Try using built-in asp.net Forms Authentication (Membership).
You can learn from these videos:
Link1 and Link2
If you want to customize it watch this video:
Link
Related
I am trying to create a Session Wrapper class for ASP.NET Core Razor Pages.
In traditional ASP.NET web forms my singleton session wrapper class used to work normally, but i am not sure if in .net core it will work the same or the same class will be shared across all requests.
I want to store specific information for each request (session) and not to be shared across all requests.
i created the following wrapper :
public class MySession
{
//the ISession interface mandatory
public ISession Session { get; set; }
private static MySession instance;
private MySession() {
}
public static MySession Instance
{
get
{
if (instance == null)
{
instance = new MySession();
}
return instance;
}
}
//properties
public User User
{
get => SessionHelper.SessionExtensions.Get<User>(this.Session, "User");
set => SessionHelper.SessionExtensions.Set<User>(this.Session, "User", value);
}
public bool LoggedIn
{
get => SessionHelper.SessionExtensions.Get<bool>(this.Session, "LoggedIn");
set => SessionHelper.SessionExtensions.Set<bool>(this.Session, "LoggedIn", value);
}
}
and from my page model (Login.cshtml.cs) i am doing the following :
public void OnGet()
{
MySession.Instance.Session = HttpContext.Session;
}
and i am accessing the session and storing and retrieving information perfectly as expected for example :
public ActionResult OnPostAuthenticate()
{
string username = Request.Form["username"];
string password = Request.Form["password"];
User user = this.userDataManager.GetUserAuthentication(username, password);
if (user != null)
{
//authentication success
//save session variables
MySession.Instance.User = user;
MySession.Instance.LoggedIn = true;
return new JsonResult(new { success = true });
}
else
{
return new JsonResult(new { success = false });
}
}
And as i said everything work perfect, but i want to know if it is SAFE to use the session this way or my requests will be messed up and the same session information will be shared across all requests ?
There are many questions about custom authentication in ASP.NET, but none of them answers how to fully integrate it with ASP.NET mechanics.
I want to make a web application for system which already exists and have users.
Let's create new ASP.NET MVC project. I choose "No authentication" to make it from scratch (because I need to read from custom tables, custom hashing function etc, so I'll go this way).
I'll use IIdentity and IPrincipal interfaces to carry logged in user in HttpContext.
public class Identity : IIdentity
{
public Identity(string name)
{
Name = name;
IsAuthenticated = true;
}
public string Name { get; private set; }
public string AuthenticationType { get; set; }
public bool IsAuthenticated { get; set; }
}
public class Principal : IPrincipal
{
public Principal(string email)
{
Identity = new Identity(email);
}
public IIdentity Identity { get; private set; }
public bool IsInRole(string role)
{
return false;
}
}
I'll create SessionsController which will create and destroy Session. Session will contain Id of logged in user.
public class UserManager
{
public bool Authenticate(WorkerDTO user, string password)
{
using (var context = new DatabaseContext())
{
var user = context.Users.SingleOrDefault(w => w.Email == user.Email);
if (user != null)
{
// compute SHA512 of password with salt
var hash = Hash(user.Password);
if (user.HashPassword == hash)
return true;
}
return false;
}
}
}
public class SessionsController : Controller
{
private IDatabaseManager _dbManager;
private UserManager _userManager;
public SessionsController(IDatabaseManager dbManager, UserManager userManager)
{
_dbManager = dbManager;
_userManager = userManager;
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel model)
{
var user = _dbManager.Give(new WorkerByEmail(model.Email));
if(user != null && _userManager.Authenticate(user, model.Password))
{
// create session
Session["UserId"] = worker.Id;
// assign User property
User = new Principal(worker.Email);
return RedirectToAction("Index", "Home");
}
return RedirectToAction("New");
}
public ActionResult Logout()
{
Session.Remove("UserId");
User = null;
return View();
}
}
Application won't remember that User = new Principal(worker.Email);.
So, if I want to make use of sessions, I want to tell my ASP.NET application that User behind UserId carried by session (probably a cookie) each request is the logged in user. In Global.asax I have available events with reasonable names:
AuthenticateRequest
PostAuthenticateRequest.
Unfortunately, Session is unavailable in these events.
How to make use of ASP.NET in my application? Maybe Session isn't the way to go?
Should I create new IPrincipal object and attach to User prop each request?
And why User property isn't null at start? Should I set it to null on logout?
Shibboleth is a SSO Authentication that is added to IIS as a "plugin".
After a user has done a Login there are Headers showing the Shibboleth Session:
ShibSessionID
ShibIdentityProvider
eppn
affiliation
entitlement
unscopedaffiliation
...more
So i can extract username and roles from the Headers.
so far so fine.
Question:
How can I implement a handler that does read the headers and set the status that a user is authorized?
Idea is to use the
[Authorize]
Attribute and the Method
Roles.IsUserInRole.
All from the Headers, no Database, no User Management.
Update
Implementation According to the Answer from #Pharylon
In this Update there is nothing new, just a help for the copy&past friends.
Of course you have to adjust the properties and Header fieldnames according to your Shibboleth Setup.
File: ShibbolethPrincipal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal; //GenericPrincipal
namespace Shibboleth
{
public class ShibbolethPrincipal : GenericPrincipal
{
public string username
{
get { return this.Identity.Name.Replace("#ksz.ch", ""); }
}
public string firstname
{
get { return HttpContext.Current.Request.Headers["givenName"]; }
}
public string lastname
{
get { return HttpContext.Current.Request.Headers["surname"]; }
}
public string phone
{
get { return HttpContext.Current.Request.Headers["telephoneNumber"]; }
}
public string mobile
{
get { return HttpContext.Current.Request.Headers["mobile"]; }
}
public string entitlement
{
get { return HttpContext.Current.Request.Headers["eduzgEntitlement"]; }
}
public string homeOrganization
{
get { return HttpContext.Current.Request.Headers["homeOrganization"]; }
}
public DateTime birthday
{
get
{
DateTime dtHappy = DateTime.MinValue;
try
{
dtHappy = DateTime.Parse(HttpContext.Current.Request.Headers["dateOfBirth"]);
}
finally
{
}
return dtHappy;
}
set {}
}
public ShibbolethPrincipal()
: base(new GenericIdentity(GetUserIdentityFromHeaders()), GetRolesFromHeader())
{
}
public static string GetUserIdentityFromHeaders()
{
//return HttpContext.Current.Request.Headers["eppn"];
return HttpContext.Current.Request.Headers["principalName"];
}
public static string[] GetRolesFromHeader()
{
string[] roles = null;
//string rolesheader = HttpContext.Current.Request.Headers["affiliation"];
string rolesheader = HttpContext.Current.Request.Headers["eduzgEntitlement"];
if (rolesheader != null)
{
roles = rolesheader.Split(';');
}
return roles;
}
}
}
File: ShibbolethController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Shibboleth
{
public class ShibbolethController : Controller
{
protected new ShibbolethPrincipal User
{
get
{
return (base.User as ShibbolethPrincipal) ?? null; //CustomPrincipal.GetUnauthorizedPrincipal();
}
}
}
}
File: Global.asax
void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var ctx = HttpContext.Current;
var principal = new ShibbolethPrincipal();
HttpContext.Current.User = principal;
}
Using examples:
namespace itservices.Controllers
{
[Authorize] //examples : [Authorize(Roles="Administrators")], [Authorize(Users="Alice,Bob")]
public class PasswordMailController : ShibbolethController
{
if(User.IsInRole("staff"))
{
You'll want to create a method in Global.asax.cs that has the following signature
protected void Application_PostAuthenticateRequest()
{
//Your code here.
}
This will be called automatically before almost anything else is done (MVC will call this method if it exists, you don't have to "turn it on" anywhere), and this is where you need to set the Principal. For instance, let's assume you have a header called RolesHeader that has a comma separated value of roles and another header called UserId that has (duh) the user ID.
Your code, without any error handling, might look something like:
protected void Application_PostAuthenticateRequest()
{
var rolesheader = Context.Request.Headers["RolesHeader"];
var userId = Context.Request.Headers["UserId"];
var roles = rolesheader.Split(',');
var principal = new GenericPrincipal(new GenericIdentity(userId), roles);
Context.User = principal;
}
It's the Principal/Identity that the [Authorize] attribute uses, so setting it here at the beginning of the request lifecycle means the [Authorize] attribute will work correctly.
The rest of this is optional, but I recommend it:
I like to create my own custom classes that implement IPrincipal and IIdentity instead of using the GenericPrincipal and GenericIdentity, so I can stuff more user information in it. My custom Principal and Identity objects then have much more rich information, such as branch numbers or email addresses or whatever.
Then, I create a Controller called BaseController that has the following
protected new CustomPrincipal User
{
get
{
return (base.User as CustomPrincipal) ?? CustomPrincipal.GetUnauthorizedPrincipal();
}
}
This allows me to access all my rich, custom Principal data instead of just what's defined in IPrincipal. All of my real controllers then inherit from BaseController instead of directly from Controller.
Obviously, when using a custom Principal like this, in the Application_PostAuthenticateRequest() method, you'd set the Context.User to be your CustomPrincipal instead of a GenericPrincipal.
I am attempting to make a Windows Forms application that plugs into some services exposed by ASP.NET MVC WebAPI, but am having a great deal of trouble with the authentication/login part.
I cannot seem to find an example that just demonstrates how to do this from Windows Forms, everything I find seems to be very convoluted and includes a lot of very deep plumbing, or seems targeted to other ASP.NET websites, and not windows forms.
Is there something I am missing? Is this just not possible? Or is it just not intended? I've looked at things like this .NET WebApi Authentication that claim to do it, but I don't see how to use cookies from a Windows Forms standpoint. I've also gone over http://blogs.msdn.com/b/webdev/archive/2012/08/26/asp-net-web-api-and-httpclient-samples.aspx and still have had very little luck.
Just create authentication token on server-side and store it in your database or even in cache. Then send this token with requests from your win forms application. WebApi should check this token all the time. It's good enough and you have full control over your auth process.
Let me share, how it works for me:
Object with Auth details:
public class TokenIdentity
{
public int UserID { get; set; }
public string AuthToken { get; set; }
public ISocialUser SocialUser { get; set; }
}
Web API Auth Controller:
public class AuthController : ApiController
{
public TokenIdentity Post(
SocialNetwork socialNetwork,
string socialUserID,
[FromUri]string socialAuthToken,
[FromUri]string deviceRegistrationID = null,
[FromUri]DeviceType? deviceType = null)
{
var socialManager = new SocialManager();
var user = socialManager.GetSocialUser(socialNetwork, socialUserID, socialAuthToken);
var tokenIdentity = new AuthCacheManager()
.Authenticate(
user,
deviceType,
deviceRegistrationID);
return tokenIdentity;
}
}
Auth Cache Manager:
public class AuthCacheManager : AuthManager
{
public override TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
if (HttpRuntime.Cache[authToken] != null)
{
return (TokenIdentity) HttpRuntime.Cache.Get(authToken);
}
return base.CurrentUser;
}
}
public int? CurrentUserID
{
get
{
if (CurrentUser != null)
{
return CurrentUser.UserID;
}
return null;
}
}
public override TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
if (socialUser == null) throw new ArgumentNullException("socialUser");
var identity = base.Authenticate(socialUser, deviceType, deviceRegistrationID);
HttpRuntime.Cache.Add(
identity.AuthToken,
identity,
null,
DateTime.Now.AddDays(7),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null);
return identity;
}
}
Auth Manager:
public abstract class AuthManager
{
public virtual TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserByToken(authToken);
if (user == null) return null;
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
public virtual TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserBySocialID(socialUser.SocialUserID, socialUser.SocialNetwork);
user = (user ?? new User()).CopyFrom(socialUser);
user.AuthToken = System.Guid.NewGuid().ToString();
if (user.ID == default(int))
{
usersRepo.Add(user);
}
usersRepo.SaveChanges();
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
Global Action Filter:
public class TokenAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.AbsolutePath.Contains("api/auth"))
{
return;
}
var authManager = new AuthCacheManager();
var user = authManager.CurrentUser;
if (user == null)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
//Updates the authentication
authManager.Authenticate(user.SocialUser);
}
}
Global.asax registration:
GlobalConfiguration.Configuration.Filters.Add(new AuthFilterAttribute());
The idea is that AuthCacheManager extends AuthManager and decorates it's methods and properties. If there is nothing inside cache then go check database.
You could use token based authentication. Here's a great article illustrating how you could write a custom action filter that uses RSA public/private cryptography.
I've been looking at ServiceStack and I'm trying to understand how to use BasicAuthentication on a service with an existing database. I would like to generate a public key (username) and secret key (password) and put that in an existing user record. The user would then pass that to the ServiceStack endpoint along with their request.
What do I need to implement in the ServiceStack stack to get this working?
I have looked at both IUserAuthRepository and CredentialsAuthProvider base class and it looks like I should just implement IUserAuthRepository on top of my existing database tables.
I am also trying to figure out what is the bare minimum I should implement to get authentication working. I will not be using the service to Add or Update user access to the Service, but instead using a separate web application.
Any help and past experiences are greatly appreciated.
Example of authenticating against an existing database (in this case via Umbraco/ASP.NET membership system). 1) Create your AuthProvider (forgive the verbose code, and note you don't have to override TryAuthenticate too, this is done here to check if the user is a member of specific Umbraco application aliases):
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Security;
using ServiceStack.Configuration;
using ServiceStack.Logging;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
using ServiceStack.WebHost.Endpoints;
using umbraco.BusinessLogic;
using umbraco.providers;
public class UmbracoAuthProvider : CredentialsAuthProvider
{
public UmbracoAuthProvider(IResourceManager appSettings)
{
this.Provider = "umbraco";
}
private UmbracoAuthConfig AuthConfig
{
get
{
return EndpointHost.AppHost.TryResolve<UmbracoAuthConfig>();
}
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
ILog log = LogManager.GetLogger(this.GetType());
var membershipProvider = (UsersMembershipProvider)Membership.Providers["UsersMembershipProvider"];
if (membershipProvider == null)
{
log.Error("UmbracoAuthProvider.OnAuthenticated - NullReferenceException - UsersMembershipProvider");
session.IsAuthenticated = false;
return;
}
MembershipUser user = membershipProvider.GetUser(session.UserAuthName, false);
if (user == null)
{
log.ErrorFormat(
"UmbracoAuthProvider.OnAuthenticated - GetMembershipUser failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
if (user.ProviderUserKey == null)
{
log.ErrorFormat(
"UmbracoAuthProvider.OnAuthenticated - ProviderUserKey failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
User umbracoUser = User.GetUser((int)user.ProviderUserKey);
if (umbracoUser == null || umbracoUser.Disabled)
{
log.WarnFormat(
"UmbracoAuthProvider.OnAuthenticated - GetUmbracoUser failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
session.UserAuthId = umbracoUser.Id.ToString(CultureInfo.InvariantCulture);
session.Email = umbracoUser.Email;
session.DisplayName = umbracoUser.Name;
session.IsAuthenticated = true;
session.Roles = new List<string>();
if (umbracoUser.UserType.Name == "Administrators")
{
session.Roles.Add(RoleNames.Admin);
}
authService.SaveSession(session);
base.OnAuthenticated(authService, session, tokens, authInfo);
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
ILog log = LogManager.GetLogger(this.GetType());
var membershipProvider = (UsersMembershipProvider)Membership.Providers["UsersMembershipProvider"];
if (membershipProvider == null)
{
log.Error("UmbracoAuthProvider.TryAuthenticate - NullReferenceException - UsersMembershipProvider");
return false;
}
if (!membershipProvider.ValidateUser(userName, password))
{
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - ValidateUser failed - {0}", userName);
return false;
}
MembershipUser user = membershipProvider.GetUser(userName, false);
if (user == null)
{
log.ErrorFormat("UmbracoAuthProvider.TryAuthenticate - GetMembershipUser failed - {0}", userName);
return false;
}
if (user.ProviderUserKey == null)
{
log.ErrorFormat("UmbracoAuthProvider.TryAuthenticate - ProviderUserKey failed - {0}", userName);
return false;
}
User umbracoUser = User.GetUser((int)user.ProviderUserKey);
if (umbracoUser == null || umbracoUser.Disabled)
{
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - GetUmbracoUser failed - {0}", userName);
return false;
}
if (umbracoUser.UserType.Name == "Administrators"
|| umbracoUser.GetApplications()
.Any(app => this.AuthConfig.AllowedApplicationAliases.Any(s => s == app.alias)))
{
return true;
}
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - AllowedApplicationAliases failed - {0}", userName);
return false;
}
}
public class UmbracoAuthConfig
{
public UmbracoAuthConfig(IResourceManager appSettings)
{
this.AllowedApplicationAliases = appSettings.GetList("UmbracoAuthConfig.AllowedApplicationAliases").ToList();
}
public List<string> AllowedApplicationAliases { get; private set; }
}
2) Register provider via usual AppHost Configure method:
public override void Configure(Container container)
{
// .... some config code omitted....
var appSettings = new AppSettings();
AppConfig = new AppConfig(appSettings);
container.Register(AppConfig);
container.Register<ICacheClient>(new MemoryCacheClient());
container.Register<ISessionFactory>(c => new SessionFactory(c.Resolve<ICacheClient>()));
this.Plugins.Add(
new AuthFeature(
// using a custom AuthUserSession here as other checks performed here, e.g. validating Google Apps domain if oAuth enabled/plugged in.
() => new CustomAuthSession(),
new IAuthProvider[] { new UmbracoAuthProvider(appSettings)
}) {
HtmlRedirect = "/api/login"
});
}
3) Can now authenticate against existing Umbraco database # yourapidomain/auth/umbraco, using Umbraco to manage users/access to API. No need to implement extra user keys/secrets or BasicAuthentication, unless you really want to....
I'm just starting with ServiceStack and I needed exactly the same thing - and I managed to get it to work today.
The absolute bare minimum for logging in users via Basic Auth is this:
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
public class CustomBasicAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
// here, you can get the user data from your database instead
if (userName == "MyUser" && password == "123")
{
return true;
}
return false;
}
}
...and register it in the AppHost:
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomBasicAuthProvider()
}) { HtmlRedirect = null });
That's all!
Another possible solution would be to use the default BasicAuthProvider and provide an own implementation of IUserAuthRepository instead.
I can show you an example of this as well, if you're interested.
EDIT:
Here's the bare minimum IUserAuthRepository - just inherit from InMemoryAuthRepository and override TryAuthenticate:
using ServiceStack.ServiceInterface.Auth;
public class CustomAuthRepository : InMemoryAuthRepository
{
public override bool TryAuthenticate(string userName, string password, out UserAuth userAuth)
{
userAuth = null;
if (userName == "MyUser" && password == "123")
{
userAuth = new UserAuth();
return true;
}
return false;
}
}
...and register it in the AppHost:
container.Register<IUserAuthRepository>(r => new CustomAuthRepository());
Of course, you need to register one of the default AuthProviders (Basic, Credentials, whatever) as well.