How to get username and password from database before user is authenticated - azure

I am using the Microsoft.Azure.Mobile.Server.Login NuGet package with ionic and visual studios to add custom authentication. The problem is I must check with my database whether or not the username and password is correct, but the user is not authenticated yet so I receive a 401 unauthorized request. How do I get around this?

You probably what to implement a promise. So when the user registers? ( i am guessing this is for registration ) you first search against the current db and the login details. This needs to return ( you can have a loading icon or something while that is happening ) then write a condition based on the return. The conditional statement only executing when the promise is returned and your logic for redirect ( or other , not sure 100% about the final outcome you want ) or rather what ever sets the 401 can be skipped in the if else
checkUser(userDetails).then(returnedVal => {
if(returnedVal){
// user exists
}else{
// user does not exist
}
});
The user does or does not exist in the Db. First check for that . Does the user exist , a singular operation with no other core functionality. Then work your code against the outcome of that single operation.

Related

REST API - How to implement user specific authorisation?

So I'm currently learning/building a REST API backend server for my web application using NodeJS, ExpressJS, and MySQL as the database. My question is in regards to the best way to implement authorisation to ensure User A does not access or edit the data belonging to another User. Please note that I understand there are a lot of examples for implementation of role based authorisation (ie user groups vs admin groups, etc) but this is not what I'm asking. Instead, how do I authorise a user against the data they are accessing?
It is possible that I'm overthinking this and this is not even necessary; that I should just check whether the data belongs to the user in every SQL query, but I thought I'd ask if there's a middleware or policy architecture that takes care of this, or maybe even authorise through caching.
The only solution I can think of is that every SQL query returns the the user id with the result, then I just create a service that checks every result if the id matches or not. If yes, then proceed. If not rollback the query and return unauthorised error. Is this ok?
I very much appreciate your advice, help, and if you can point me in the right direction.
Many thanks in advance.
Save the userId (or ownerId) in every table, and create a middleware where each db access method requires the userId as a parameter, for example:
readOne(id, userId) {
// implements SELECT * FROM example WHERE id = id AND userId = userId
}
updateOne(id, data, userId) {
// implements UPDATE example SET data = data WHERE id = id AND userId = userId
}
...
For security reasons, never send as a response "Requested data exist by you aren't the owner".
The simplest things usually work best. You wouldn't have to have a special service for checking authorization rights for every entity and you can do it at data access level eg. SELECT * FROM foo WHERE user_id = :currentUser or UPDATE foo SET foo = bar WHERE user_id = :currentUser
It also depends whether you want to notify the user about unallowed access via HTTP401 or not to reveal that such a resource even exists for different user HTTP404.
For HTTP401 the scenario would be:
const entity = loadFromDB(id);
if(entity.userId !== currentUserId) {
res.send(401);
return;
}
... update entity logic ...

Steam OpenID Signature Validation

I've been having this issue for a while now. I'm trying to add a Sign in through Steam button, which upon login, not only retrieves the user's ID, but also validates the signature. Steam uses OpenID 2.0.
I have followed the documentation here. I have followed these steps carefully, spending the better part of my day on trying to figure this out. My code is this:
let s = data['openid.signed'].split(',');
let x = Buffer.from(s.map(x => `${x}:${data['openid.' + x]}`).join('\n') + '\n', 'utf8');
let c = crypto.createHash('sha1').update(x).digest('base64');
console.log(x.toString('utf8')); // This is the key:value string
console.log(c); // This is the final result; the generated signature
Where data is the response given from the OpenID provider.
Logging x (key:value pair string) gives the expected output of:
signed:signed,op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle
op_endpoint:https://steamcommunity.com/openid/login
claimed_id:https://steamcommunity.com/openid/id/765611981[1234567]
identity:https://steamcommunity.com/openid/id/765611981[1234567]
return_to:http://127.0.0.1:8000/resolve
response_nonce:2018-12-01T17:53: [some_hash]=
assoc_handle:1234567890
However, my generated hash c does not match the given signature, openid.sig. Note that I use a \n at the end of the above key:value pair string, as that is how I interpreted the documentation.
Note. The reason why I need authentication is that I want to connect the Steam account to an account on my website, and being logged in via Steam gives you full access to your account on my website, meaning that it's of utter importance that a user cannot simply enter another users id and get access to their account (replay attack). Because of this, I need to somehow validate the signature.
I have never worked with OpenID before, so please excuse any foolish mistakes of mine. I highly recommend reading the documentation that is linked above, so that you can verify what I am doing is right.
Kinds regards,
Initial Request
Make your Steam login button link to
https://steamcommunity.com/openid/login?openid.ns=http://specs.openid.net/auth/2.0&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=https://mywebsite.com&openid.realm=https://mywebsite.com&openid.mode=checkid_setup
and replace the openid.return_to and openid.realm query string parameters.
openid.return_to: This is the URL that Steam will redirect to upon successful login with appended query string parameters.
openid.realm The URL Steam will ask the user to trust. It will appear as a message like this when the user is on the Steam login page: Sign into {openid.realm} using your Steam account. Note that {openid.realm} is not affiliated with Steam or Valve.
Handling the response
Upon successful login, Steam will redirect to a URL like
https://mywebsite.com/?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://steamcommunity.com/openid/login&openid.claimed_id=https://steamcommunity.com/openid/id/76561198002516729&openid.identity=https://steamcommunity.com/openid/id/76561198002516729&openid.return_to=https:/%mywebsite.com&openid.response_nonce=2020-08-27T04:44:16Zs4DPZce8qc+iPCe8JgQKB0BiIDI=&openid.assoc_handle=1234567890&openid.signed=signed,op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle&openid.sig=W0u5DRbtHE1GG0ZKXjerUZDUGmc=
To verify the user, make a call from your backend to https://steamcommunity.com/openid/login copying every query string parameter from that response with one exception: replace &openid.mode=id_res with &openid.mode=check_authentication. So the final call will be to this URL:
https://steamcommunity.com/openid/login?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=check_authentication&openid.op_endpoint=https://steamcommunity.com/openid/login&openid.claimed_id=https://steamcommunity.com/openid/id/76561198002516729&openid.identity=https://steamcommunity.com/openid/id/76561198002516729&openid.return_to=https://mywebsite.com&openid.response_nonce=2020-08-28T04:44:16Zs4DPZce8qc+iPCe8JgQKB0BiIDI=&openid.assoc_handle=1234567890&openid.signed=signed,op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle&openid.sig=W0u5DRbtHE1GG0ZKXjerUZDUGmc=
Steam will return a text/plain response like this:
ns:http://specs.openid.net/auth/2.0
is_valid:true
If true the user is valid, false invalid. Note this call will only return true once and subsequent calls with the same parameters will always return false. From here, you can decide how to maintain the user being logged in (such as creating a unique cookie) and return a redirect response to something like your site's homepage, last page before they clicked the Steam login button, or user detail page, etc...

How to make two factor authentication token expire after single use

I implemented two factor authentication but by following this tutorial
https://learn.microsoft.com/en-us/aspnet/identity/overview/features-api/two-factor-authentication-using-sms-and-email-with-aspnet-identity
I want to make the code expire after single use.
Right now, user receives the same code during the expiration time (which is set to 5 minutes) completes. Is there a way to make the code single use? I couldn't find anything on this subject.
There is a note in the tutorial that you linked to that says:
The 2FA codes are generated using Time-based One-time Password Algorithm and codes are valid for six minutes. If you take more than six minutes to enter the code, you'll get an Invalid code error message.
So, using this method, you cannot make the code expire after user.
You could, as an addition, keep a store of codes that have been used and check against that store before validating the code. You could allow the codes to expire out of that store after 6 minutes, which is their natural expiry time, but in the meantime use them to reject a second authentication.
Alternatively, you can choose to avoid the TOTP method and generate a random code that you store against your user before you send the SMS or email. Then you can check against that code when the user authenticates with it and delete or invalidate the code at that point. Using TOTP means that you could extend this 2FA to use an authenticator app based flow for the authentication too, which is more secure than SMS or email.
AspNetIdentity does not automatically invalidate used second factor codes, a code is always valid for a six minute window, but there is a workaround for this.
One of the inputs to the token generator is the SecurityStamp, which is stored as part of the user account. Token providers that extend the TotpSecurityStampBasedTokenProvider, like for example the EmailTokenProvider, will use the security stamp when they generate and validate a second factor code.
Thus, you can invalidate all issued tokens by changing the security stamp by calling UserManager.UpdateSecurityStampAsync(userId) after a successful two factor authentication.
There is a side effect that may not be desirable, being that other sessions will get logged out when the security stamp changes.
In the ApplicationSignInManager class, you can override TwoFactorSignInAsync and make the call there:
(Note: This is taken from AspNetIdentity, if you are using a different package, make sure to take TwoFactorSignInAsync from that instead and modify it accordingly.)
public override async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberBrowser)
{
var userId = await GetVerifiedUserIdAsync().WithCurrentCulture();
if (userId == null)
{
return SignInStatus.Failure;
}
var user = await UserManager.FindByIdAsync(userId).WithCurrentCulture();
if (user == null)
{
return SignInStatus.Failure;
}
if (await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())
{
return SignInStatus.LockedOut;
}
if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code).WithCurrentCulture())
{
// When token is verified correctly, clear the access failed count used for lockout
await UserManager.ResetAccessFailedCountAsync(user.Id).WithCurrentCulture();
// Update the security stamp in order to invalidate all issued two factor tokens.
await UserManager.UpdateSecurityStampAsync(user.Id);
await SignInAsync(user, isPersistent, rememberBrowser).WithCurrentCulture();
return SignInStatus.Success;
}
// If the token is incorrect, record the failure which also may cause the user to be locked out
await UserManager.AccessFailedAsync(user.Id).WithCurrentCulture();
return SignInStatus.Failure;
}
If you want only the latest issued code to be valid, you should make the call to UpdateSecurityStampAsync also before any new code is generated.

sailsjs waterlock get jwt on login successful and signup with more fields

I am using waterlock for authentication in sailsjs application. Everything is working fine. I need two customization.
Here to get token we need to follow two steps. http://example.com/auth/login and then http://example.com/user/jwt . What we need is token should return after successful login , i.e. http://example.com/auth/login should return token after successfull login.
When user try to login with credential by this URL : http://example.com/auth/login
If email id is not present waterlock create a new user with this email. We need to stop this because we have another mandatory fields to sign up a user like name, phone number etc.
Please tell me what needs to be done here.
I had exactly these issues yesterday as well and I was able to solve them:
I needed the user as well as the token on loggin. Go into your config/waterlock.js and look for the postAction on login and change the section to the following. Change success: 'default' to success: 'jwt'.
If you dont want to create a user if it doenst exist yet edit the following line in config/waterlock.js in the authMethod: createOnNotFound: false
I hope this helps.

Logout on ServiceStack v4

I have ServiceStack v4 service but when I call the auth/logout route (using either POST or GET) to logout the currently logged-in user, I get an error:
400 Not Empty
User Name cannot be empty
Password Cannot be empty
As I wouldn't expect users to enter credentials when logging out, I am surely missing something?
I have the AuthFeature registered during host initialisation, and I am using CredentialsAuthProvider. I have taken the code from Github so I can see how it works.
My Client Code:
var rest = Restangular.one('auth/logout').get();
//var result = rest.post({userName: userName});
this.requestTracker.addPromise(rest);
return rest;
After a lot of digging, this happens when you are using CredentialsAuthProvider. Within this class, a validator is defined that validates all instances of the Authenticate request. As the logout route uses the Authenticate request, this validator is fired.
I got round it by modifying the validator to:
RuleFor(x => x.UserName).NotEmpty().When(d => d.provider != "logout");
RuleFor(x => x.Password).NotEmpty().When(d => d.provider != "logout");
This is probably not the most elegant way of fixing long term, but got me up and running.
I know this question is old, but I recently have been struggling with the same thing. What occurs is that before the Authenticate.Post function is called, the validation cache is checked and the CredentialsAuthProvider which has the mentioned validator fails unless username and password are not empty.
Now, i'm not sure if it makes a difference if you only have that provider enabled or not - I've not tested. I actually have my own custom provider that subclasses CredentialsAuthProvider and it's the only one I register.
The only way currently is to either pass a non-empty (but useless) password and username, or modify your own custom provider, overriding the Authenticate function and using a modified version of the validator as mentioned above.

Resources