Optemizing basic auth: Save user authentification for follow up requests - asp.net-web-api

I'm using the this tutorial to secure my Web-API calls with basic auth. Basically it checks if there is a auth header on the request and then proves this header against a database:
public static bool CheckPassword(string user, string password)
{
//Do a Database checkup
if(CheckDB(user,password)) {
//if true, set the principal
var identity = new GenericIdentity(user);
SetPrincipal(new GenericPrincipal(identity,null));
}
else {
//return 401...
}
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
This is working fine. But it queries the database for every request I do. Even when I just request a JavaScript file.
I like to optimize this process and just call CheckDB() on the first request. All following request should not need another database request. Is it possible to save the Principal? I tried to check Thread.CurrentPrincipal but it seams to reinitialize on every request.

You have a couple of options:
If you have a simple topology with only a single machine handling requests for a relatively small number of users you could implement an in memory cache of usernames and passwords that you can use to efficiently validate subsequent calls. You could implement this using something like ConcurrentDictionary that keys off the username and gives the password, although there are security considerations to such an approach (do you really want passwords to be in memory all the time?).
Set a cookie after a username/password pair has been validated. The cookie could contain something like the timestamp after which the username/password should be revalidated, along with some kind of hash that's generated by a means that only the server knows (and that it can use to validate that it set the timestamp).
With either of these approaches the "CheckPassword" method would either compare the password with the one from the cache, or checking the cookie, and if the result is satisfactory directly create a new principal without calling the database.

Related

Migrating User to Cognito on Sign In

I am trying to migrate users to Cognito when they sign in the first time. For this I wrote a lambda function that does call an API to check if the users exist in db or not ? if the user exists, it will be created in cognito but I am not sure how do I tell the application that user is created and it should allow the user to login .
Here is the code in c#:
public async Task<Stream> FunctionHandlerAsync(Stream stream, ILambdaContext context)
{
RootObject rootObj = DeserializeStream(stream);
User user = new User(rootObj.userName, rootObj.request.password);
ApiResponse apiResponse = await MobileAuthenticateAsync(user.UserName, user.Password);
// Considering apiResponse returns "user authenticated", we create the user in //cognito. This is working.
// How do I send response back to Application so it knows that user is // //created and authenticated and should be allowed to login.
//Before returning stream, I am setting following 2 status.
rootObj.response.finalUserStatus = "CONFIRMED"; // is this correct ?
rootObj.response.messageAction = "SUPPRESS";
return SerializeToStream(rootObj);;
}
You're pretty close.
You can see the full documentation on the Migrate User Lambda Trigger page, however in short you need your response to look like:
{
response: {
userAttributes: {
email: 'user#example.com',
email_verified: true,
custom:myAttribute: 123,
},
finalUserStatus: 'CONFIRMED',
messageAction: 'SUPPRESS',
forceAliasCreation: false,
}
}
Where:
userAttribute: this is a dictionary/map of the user's attributes keys in cognito (note that any custom attributes need to be prefixed with custom:), to the values from the system you're migrating from. You do not need to provide all of these, although if you're using an email alias you may want to set email_verified: true to prevent the user having to re-verify their e-mail address.
finalUserStatus: if you set this to CONFIRMED then the user will not have to re-confirm their email address/phone number, which is probably a sensible default. If you are concerned that the password is given as plain-text to cognito this first-time, you can instead use RESET_REQUIRED to force them to change their password on first sign-in.
messageAction: should probably be SUPPRESS unless you want to send them a welcome email on migration.
forceAliasCreation: is important only if you're using email aliases, as it stops users who manage to sign-up into cognito being replaced on migration.
If you respond with this (keeping the rest of the original rootObj is convenient but not required then the user will migrated with attributes as specified.
If you throw (or fail to respond with the correct event shape) then the migration lambda fails and the user is told that they couldn't migrated. For example, because they do not exist in your old user database, or they haven't provided the right credentials.

What is a good way to pass additional information to the http response when issuing access token with Owin middleware in Asp Net Web Api?

I am using Owin middleware to implement token-based security for my application. When issuing the access token to the client I would also like to pass User Id along with the token, so that, the client application will know the User Id and will be able to call GetUserById (one of the methods inside UserController) in order to show the user his starting page. The best solution I could come up with so far is just adding User Id to the response header. Take a look at my OAuthAuthorizationServerProvider class, in GrantResourceOwnerCredentialsmethod I am adding User Id to the header, using context.Response.Headers.Add("User-Id", new string[] { "1" })
Here is the implementation of my OAuthAuthorizationServerProviderclass
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
//The actual credential check will be added later
if (context.UserName=="user" && context.Password=="user")
{
identity.AddClaim(new Claim("Id", "1"));
context.Validated(identity);
//Here I am adding User Id to the response header
context.Response.Headers.Add("User-Id", new string[] { "1" });
}
else
{
context.SetError("invalid_grant","The credentials provided are not valid");
return;
}
}
}
Basically the client then will have to read User-Id from the header. Is this a good solution I came up with or there is a better one? Also what if I want to pass the whole User object with all its properties to the response is it possible and how to do this?
Since you store the ID already in the claims, why don't you just decode your token on the client and read out the user id like that? Passing it through the headers could allow tampering with it (security).
Have a look on how you could achieve to decode your token and read the claims. This is a c# example https://contos.io/peeking-inside-your-jwt-tokens-using-c-bf6a729d06c8 but this could also be done even through javascript.
This, assuming you use the JWT-format as token (was not specified in your initial question).
Bad way to store UserID as a response header back to client side. This is a huge security concern.
The best way would be to store it as a Claims.
It is very easy to achieve this and get back the claims in the client side.
In your controller, call this and remember to reference
using Microsoft.AspNet.Identity;
var userId = User.Identity.GetUserId();

Custom session not working with Authenticate

I'm trying to design a solution where a ServiceStack server can just use an authentication cookie from ASP.NET. (In reality, it could be any cookie. It's just getting a session ID that it can lookup details using a back channel). The custom auth providers don't seem to be the right direction since they are based on credentials being sent. Instead, a GlobalRequestFilter made more sense to me. In there, I check the cookie, get the external session information, then set them to the ServiceStack session and set IsAuthenticated. This works fine in the request service as it has access to the session details that it needs. Fine so far.
The issue, is that when I decide to lock down services with the Authenticate attribute, it apparently runs the attribute prior to my filter so it always wants to redirect them to login. What is the recommended place to add my logic so it fires before the Authenticate attribute and validates properly?
ServiceStack's [Autenticate] attribute is for use with ServiceStack's AuthProvider model so you'll still want to use a Custom AuthProvider. You can have a look at the IAuthWithRequest Auth Providers in the last release notes for examples of creating Custom Auth Providers that aren't based on using credentials:
JwtAuthProviderReader.cs
ApiKeyAuthProvider.cs
AspNetWindowsAuthProvider.cs
By implementing IAuthWithRequest interface in your AuthProvider the [Authenticate] Request Filter will call PreAuthenticate() to perform any Auth validation before validating whether the User is Authenticated or not. Here you can populate the Users Session if the User is Authenticated, e.g:
public class MyAuthProvider : AuthProvider, IAuthWithRequest
{
public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
{
return session.IsAuthenticated;
}
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
{
throw new NotImplementedException("Authenticate() should not be called directly");
}
public void PreAuthenticate(IRequest req, IResponse res)
{
//Do any Auth validation...
//populate the Session in the Request to Authenticate this user
req.Items[Keywords.Session] = new AuthUserSession {
UserName = ...,
Email = ...,
//populate other fields
IsAuthenticated = true,
};
}
}
Then to register your custom Auth Provider add it to your AuthFeature plugin in AppHost.Configure(), e.g:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new MyAuthProvider (),
}));

Can I store an access Cookie in a Laravel session?

I am working with a remote API that is normally accessed directly via JavaScript. In the normal flow, The user authenticates by sending Auth headers and in return is granted a cookie.
What I am trying to do is send auth headers from a laravel app, authenticate in the app controller, and provide API access through laravel controller functions.
I was hoping this would be as simple as authenticating and sending my subsequent API calls, hoping that the cookie given to the PHP server would continue to grant authentication.
Well that doesn't work and thats fine, but now I am thinking that I need to store my access cookie in the Session, and send it in the headers for future API calls.
Will this work/how can I go about this? My supervisors don't want to implement OAuth type tokens on the remote server and to me that seems like the best route, so I am a bit stuck.
Cookies cannot be shared across multiple hosts. The cookie (on the client) is only valid for path which set it.
EDIT - ADDING ADDITION AUTH DETAIL
Setting up remember me in Laravel
When migrating (creating) you User table add $table->rememberToken()
to create that column in your User table.
When user signs up to your service add a check box to allow them to
make the decision OR you can just set it true if you don’t to offer
the user the option as described in step 3
< input type="checkbox" name="remember" >
In your controller you add the following code:
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// The user is being remembered...
}
Users table must include the string remember_token column per 1. , now assuming you have added the token column to your User table you can pass a boolean value as the second argument to the attempt method, which will keep the user authenticated indefinitely, or until they manually logout. i.e. Auth::attempt([$creditentials], true);
Side note: the Illuminate\Contracts\Auth\UserProvider contract, public function updateRememberToken(Authenticatable $user, $token) uses the user’s UID and token stored in the User table to store the session auth.
AUTH ONCE:
Laravel has once method to log a user into the application for a single request. No sessions or cookies. Used with stateless API.
if (Auth::once($credentials)) {
//
}
OTHER NOTES
The remember cookie doesn't get unset automatically when user logs out. However using the cookie as I explained below in cookies example you could add this to your logout function in your controller just before you return the redirect response after logout.
public function logout() {
// your logout code e.g. notfications, DB updates, etc
// Get remember_me cookie name
$rememberCookie = Auth::getRecallerName();
// Forget the cookie
$forgetCookie = Cookie::forget($rememberCookie);
// return response (in the case of json / JS) or redirect below will work
return Redirect::to('/')->withCookie($forgetCookie);
OR you could q$ueue it up for later if you are elsewhere and cannot return a response immediately
Cookie::queue(forgetCookie);
}
Basic general cookie example that might help you. There are better approaches to do this using a Laravel Service provider
// cookie key
private $myCookieKey = 'myAppCookie';
// example of cookie value but can be any string
private $cookieValue = 'myCompany';
// inside of a controller or a protected abstract class in Controller,
// or setup in a service ... etc.
protected function cookieExample(Request $request)
{
// return true if cookie key
if ($request->has($this->myCookieKey)) {
$valueInsideOfCookie = Cookie::get($this->myCookieKey);
// do something with $valueInsideOfCookie
} else {
// queue a cookie with the next response
Cookie::queue($this->myCookieKey, $this->cookieValue);
}
}
public function exampleControllerFunction(Request $request)
{
$this->cookieExample($request);
// rest of function one code
}
public function secondControllerFunction(Request $request)
{
$this->cookieExample($request);
// rest of function two code
}

MVC 3 FormsAuthentication and disabled user accounts

I have noticed that if a user is still logged in or has a persistent cookie, even if he gets "banned", or disabled in the database (Users Table flags), the user can still access everything until that cookie goes away or the user logs out of the site. Great security right.
So I am putting together a ActionFilterAttribute that checks for this, the disturbing thing for me is I have to hit the database for every controller that his ActionFilterAttribute is applied to. There has to be a better way of doing this but I have not found one yet.
Any ideas would be awesome..
There has to be a better way of doing this but I have not found one yet.
No there isn't. Sorry. If the notion of disabled/banned user exists only in your database there is no other way but hitting your database. ASP.NET only verifies the validity of the authentication cookie which is sent on each request. It doesn't even know what a disabled user means so you cannot expect it do more than it already does.
There are a few options:
1) You can validate whether the user authentication is valid by hooking session start. This way if the user has a persistent cookie, you can validate the username and expire the cookie if needed.
2) You can use a time based mechanism to check the user auth status every few requests (every 5mins or whatever). You could store the lastChecked timestamp value in the user session or in the auth cookie itself using the UserData field. This allows you recheck if the user auth cookie needs to be expired more frequently, but keeps database calls to a minimum.
MyThis is the solution I came up with:
In the User Account Membership service add a function to return whether the user's account is still active.
public class UserAccountMembershipService : IMembershipService
{
public bool UserIsActive(Guid userId)
{
if (userId == new Guid()) throw new ArgumentException("Value cannot be null or empty.", "userName");
MembershipUser user = _provider.GetUser(userId, true);
return user.IsApproved;
}
}
Override the AuthorizeAttribute as follows:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
IMembershipService membershipService = new UserAccountMembershipService();
//Check to see if the user's account is still active
bool isActive = false;
if (httpContext.User.Identity.IsAuthenticated)
{
Guid userId = (Guid)Membership.GetUser(httpContext.User.Identity.Name).ProviderUserKey;
isActive = membershipService.UserIsActive(userId);
}
if (!isActive)
{
//If the user's account is no longer active log him/her out
IFormsAuthenticationService FormsService = new FormsAuthenticationService();
FormsService.SignOut();
}
//Call the base AuthorizationCore method
return base.AuthorizeCore(httpContext) && isActive;
}
}

Resources