On my site, I intend to offer users the ability to authenticate via OAuth. I don’t want to ask them to first register with me and then connect an external account; I want to offer single sign on.
I believe we’re supposed to reuse Access Tokens; certainly within sessions and even between them.
Google goes so far as to say they’ll limit the number of access tokens to 10 per user per application. (Apparently Google still supports OAuth1, but recommends Auth2 now) 10 is a pretty small number.
Using cookies (like this) seems like a good plan for identifying a user between sessions, but I’m having trouble with the scenario where a user has deleted cookies or connects from a new machine.
How do I know who the user is before I’ve requested another Access Token for them? Request tokens do not contain the userid, right?
Thanks
You will have to maintain your own user accounts anyway, no matter which protocol and which provider you choose. A token (or a URL in the case of OpenID) that you get from a provider is unique for a given user and you are supposed to associate it with your internal user account and recognize user by it.
If you don't want to provide any registration UI it's okay: just get the token, retrieve all the user info you need from the provider and store all this somewhere in your database. You will also have to issue and recognize your own cookie for your users, or else they'll be forced to go through provider auth every time they visit your site.
Related
I stumbled upon a method of securing user accounts which basically automates the user's password. From a user perspective I think it's much better than a conventional passwords and I think also from a security perspective it would be preferred over conventional passwords.
I'm developing a PHP based application that will be a companion app for a video game. Which is only relevant to explain the link I'm going to post for others to test the method I'm proposing. It involves multiple files so posting a link and asking people to try the method is the best "explanation" that I can provide but I will brief the method also.
The user provides an email address to create an account. The program generates an email with a link in it. When the user follows the link in their email, an access cookie is set on their device.
The user is given control over the cookies which grant access to their account. The user can revoke access cookies and set new access cookies on other devices.
The program also compares access cookies and fingerprints, and will revoke access cookies when the device's access cookie and fingerprint don't match with the database.
Revoking a cookie has two effects. The database connection between the device and the account is updated so the revoked cookie no longer provides access to any accounts, and the revoked cookie is subsequently unset from any devices it's found on.
Included is a picture of the user account page which shows the Access List, the list of cookies that currently grant access to this user account. The user can revoke access from this page or send another email to set a cookie on another device. The image demonstrates an account which has two access cookies granting access to the account from two devices.
The user or program can revoke all access cookies and still the user can send an email to the address associated with their account and easily regain access to their account.
Depending on the device fingerprinting method, the user will need to reset cookies on devices after browser updates or changing plugins.
On the positive, they don't need another password. Passwords are commonly referred to as the "weak" link in an account and this method essentially automates, refreshes and maintains the integrity of passwords through cookies and an associated email account. As I mentioned in the beginning, from the user perspective I think this experience was much better than a conventional password system. So now I want to learn how this method is vulnerable and how those vulnerabilities might be mitigated. I understand that this question is less concrete than what is expected for stackoverflow but if not here then where do I get this feedback? Links?
Obviously the actual code may be a security risk but I thought we could get to that level of detail later? If you're interested please try the implementation at the link. The site is themed after a video game so please be patient with the color scheme, only the account control feature is working right now. Navigate to the account page, enter a fake username and a real email address and then if you have multiple devices please try setting access cookies on them. I have verified the program working as intended on 2 Windows/Chrome devices and 1 Android device. None of your information will be retained.
development url
I am integrating Google's login Apis on a website, and I need to place the user's details in our databases to use it the next time they login.
Googles developer documents clearly out lines that developers should never store user IDs in a database, instead you should use Token IDs generated by google to auth the legitimacy of the user.
I completely agree with this, but a token's live cycle is only a short period of time. If we insert the token in our databases, the next time the user logs in, they token will be different to that in our database. So how do we auth users via token id with google sign in?
I've read all of googles developer docs https://developers.google.com/identity/sign-in/web/backend-auth and theres nothing specifically explaining this, other than their process of authentication.
Could someone please help who may have had experience in this ?
Thanks alot
This is googles warning
Warning: Do not accept plain user IDs, such as those you can get with
the GoogleSignInAccount.getId() method, on your backend server. A
modified client application can send arbitrary user IDs to your server
to impersonate users, so you must instead use verifiable ID tokens to
securely get the user IDs of signed-in users on the server side.
Googles developer documents clearly out lines that developers should never store user IDs in a database
No, they don’t.
They are telling you that your server should not trust user ids send to it directly by the client - because anyone could easily fake those.
Instead, you are supposed to send the token, that you acquired on the client side, to the server (those tokens can’t be “guessed”, therefor you can not simply fake them) - and then you make a server-side API call using that token, to get the user id.
I am working on integrating Google Apps into my PHP app. I have already a login system that assigns a session ID to a user (after entering username and password), which gets stored in the database when the user is logged in. Session ID's become invalid after a certain time of inactivity (configurable by the user, can be 5 minutes, 15, 60...). That session ID is passed in the url to check if a user is still logged in. When logging out, the session ID is removed from the database.
I let people log in with Google by storing their Google ID in the database, when they log in, I request an access token, query the userinfo, see if the google ID is in the database and if so, assign a session ID to this user. Since I want to be able to query other API's I also store the access token json in the database. When a user logs out, the access token is also removed from the database.
This works, my users are able to log in using their Google account and I can query the API's using the stored access_token, however some things feel clunky of make me feel uncertain about my workflow:
If you force_approval you get a refresh_token, I feel like I should be using this refresh token to get a new access token, instead of removing the old one from the database and entering a new one when the user logs in again. On the other hand, when logging in, I do not know who it is yet, so I don't know which refresh token to use. Maybe I'm misunderstanding what the refresh token is for. Also, I don't really want to force approval every time, so I can't even use the refresh_token in that case.
As said before, users can determine how long their session will last, however, the google access_token always expires after 3600 seconds. It'd be really stupid if users would work an hour on the system and after that the Google API's suddenly fail, forcing them to log in again. The Google OAuth playground shows a checkbox "Auto-refresh token before it expires", but I'm not seeing how to do this. Do I have to use the refresh token here? Or simply request a new token in the background (if I'm not forcing approval)?
At the moment, I'm using the userinfo query (https://www.googleapis.com/oauth2/v2/userinfo) to find the user id, but I can also use the tokeninfo (https://www.googleapis.com/oauth2/v1/tokeninfo). Tokeninfo is not listed in the oauth playground, but the result does show how long the token remains valid (however, I can also calculate this myself). Is one preferable over the other?
I'm storing the entire json object in the database (access_token, id_token, expires_in and token_type) but I feel my app will still work perfectly if I only store the access_token (only problem I foresee is if the expires_in time changes). Do I need to store the id_token for example?
I find the Google documentation (at developers.google.com) sometimes very lacking, if anyone knows any other good sources of information, I'm interested in them as well.
I think it might help if you took a look at the lastest OpenID Connect Specs where concepts like the userinfo endpoint come from. OpenID connect is built on top of OAuth 2. There's quite a lot in there, but it's still probably worth a look. This blog article is also very good (as are others in the same blog).
Unfortunately, I don't think Google's implementation is currently up to date with the latest spec draft so it will probably be a moving target for some time. These things have changed a lot over the past year.
I agree with your first point that you should be obtaining a new access token each time you authenticate a user, rather than refreshing an old one. You don't know who the user is until they have logged in and granted you an access token. In general, the lifespan of an access token is not linked to the user's session. Once issued, your application could theoretically use it to access resources independently of the user's presence. If you want to carry on accessing the resource beyond the token expiry time, then you need to submit the refresh token at that point to obtain a new access token. I'm afraid I don't know what the "auto-refresh" feature is for.
I believe Google's tokeninfo is analogous to the check_id endpoint of OpenID connect, but accepts either an access token or an id token, rather than just the latter. Note that the expiry times of the two may differ. You would typically be able to retrieve more detailed user data from the userinfo endpoint than from check_id, which would normally return the bare user_id.
You shouldn't need to store the id_token. It is a bit like a record of the user's authentication by the authorization server. The access token is what your application will be interested in maintaining once you have validated the user identity.
We are building a PHP multi-tenant application. Each company's account will run on their own subdomain abcorp.example.com. The application allows companies to write and publish content (faqs, etc) for their customers to read.
They will tell their customers to visit: abcorp.example.com/ to read the content. Or they will put a link to that URL in their secure web application.
However these companies may not want just anyone reading the content by going to abcorp.example.com/
So, the question I have is there any way to provide some basic authentication without getting into username and password authentication. I was thinking about some kind of hidden token added to the hyperlink or something like that
My goal:
If users type abcorp.example.com/ directly in the browser, they will not be able to see the web page because they didn't authenticate or pass the token in.
Avoid using username and passwords
Another option would be Referring URL Authentication
Of course, if someone makes the token public, it will open up access to whoever finds it.
I suppose each company could link to their page using a shared token, for example:
abccorp.example.com/?t=4rrfwr23rwads3
Each token could be stored in a file or a database.
When someone requests a page, it checks the value of $_GET['t'] with the one stored on the server. If it matches, it loads the rest of the page. Of course, this variable would have to be carried throughout the site, and included in every link.
Again, this will not be very secure. An exposed token could give access to the site to the entire world.
Your "hidden token" idea is essentialy the way sessions work. A session can be used to identify a user (ie. keep track of what a user does as they browse through the site), and is propagated either by passing the session ID along in links or by storing it in a cookie.
However, using a session without any other sort of authentication is inherently insecure! When you expose the way to authenticate and track users to the user itself, the user can modify or forge their authentication. For instance, the user could change the value passed along for the session ID or change the value stored in the cookie.
Please read the PHP manual section on sessions and security.
Client-side certification. Check out http://en.wikipedia.org/wiki/Mutual_authentication.
You could also use the clients IP address as a token, giving different IP addresses access to different (parts / instances) of the system. But gain, this is not very secure, as
you have no way of knowing who is behind the client PC and
IP addresses can be spoofed. Perhaps you could develop additional specs; giving IP addresses only access during office hours, or check the clients browser (user agent) and check it against the user agent officially being used at the client.
You can use basic hashing whereby a shared secret password or "key" is stored on your system and each company system (a different key for each company and not published publicly), and then you hash the secret password with the subdomain in the link and include the digest as a parameter. Then you validate it by running the same algorithm on your side and compare to the digest.
the link might look something like
abc.example.com/?d=b5939ca22f5dcf345b4000641995478c5910dbd1607b1bdadcbf4a8618a95211
where digest is:
$d = hash('sha256', $secret_password.$subdomain);
or including the referer:
$d = hash('sha256', ($secret_password.$subdomain.$_SERVER['HTTP_REFERER']));
The hurdle to get over is making sure each of the companies can support the correct generation of these links based on the company specific key/algorithm - and that it is different for each company so one company cannot produce links for another.
It is better than no authentication, or a public shared token that is not validated at all, but I'm sure it still has vulnerabilities.
I'm working on a web project that isn't all that dissimilar in principal to power.com, where I am attempting to unify several different social networking sites under a single website, allowing users to register once with the system, and then add as many of their individual social networking accounts (Facebook, MySpace, Orkut, etc) as the system is built to handle, allowing them to browse their respective profile information in a single place.
Simply put, I can't seem to find a way to authenticate arbitrary users into their social network accounts.
I've been poring over the OpenSocial specifications, as well as the OpenSocial PHP client project, but I seem to be missing something, as everything is appearing to be circularly dependent.
My first problem is that, for testing purposes, I have a MySpace consumer key and consumer secret, but whenever I attempt to perform a 3-legged authentication with MySpace, there's no option for logging in as someone else. Plus, it performs an external redirect, which is somewhat undesirable (as a user of this eventual social networking "portal", I'd rather not have to go through that redirection process every time I add a new account).
How would I programmatically authenticate an arbitrary user and allow them access to their account information (preferably without the external redirection)?
Second, the 2-legged authentication requires a userId (usually an arbitrary integer) that identifies the arbitrary user to retrieve information for. However, when I enter my MySpace OpenSocial ID, along with the given consumer key and consumer secret, I am given a 401 Access Denied error. Furthermore, in order to use this ID in the future, it seems that I would need to authenticate the user first...but that authentication appears to require the ID.
I'm pretty convinced that I'm missing something trivial, but for the life of me can't figure out what it is. Help is greatly appreciated!
Technically this isn't my answer, but the developers at OpenSocial have provided me with the following information regarding my question (emphasis mine):
3-legged OAuth is built around the
redirect back to the site you're
authenticating with, and there's no
way to avoid it. It's not the most
convenient experience, but allows
users to share their data with your
website while keeping their passwords
private. Any design which requires
users to enter their MySpace password
into a form on your website is
considered an anti-pattern and should
be avoided. You could potentially
attempt the redirect in a popup window
in order to make the experience a bit
less jarring for the user (currently
the PHP client doesn't make this that
easy, but if you followed up at
opensocial-client-libraries#googlegroups.com
someone could help you work through
that process).
With regard to not being able to
change the user, what I believe
MySpace is doing in your case is
checking for a MySpace cookie and
pre-populating your account
information. If you were a user
visiting the site and not logged into
MySpace, you should get a full
username/login box combination. There
should also be a button or link
somewhere to say "I'm not this user"
and log in with other credentials.
As for 2-legged, you would need to
have the application associated with
the consumer key/secret installed on
the profile of any user whose data you
wish to access. 2-legged is mostly
intended for developers who are
currently running a social gadget on a
container and wish to access social
data for their application users out
of band with a gadget render. In this
case, the application server would
already have the user's OpenSocial ID
(from a signed makeRequest) and the
user would already have the app
installed on their MySpace profile).
Most of this is covered
in http://wiki.opensocial.org/index.php?title=OAuth_Use_Casesif
you want more information.
Essentially, this makes any use of 2-legged authentication on an external application impossible; 2-legged was explicitly designed not to be used in this sort of situation. Furthermore, it seems that power.com is indeed employing the anti-pattern of having users supply their actual Orkut/MySpace/etc credentials, so that explains that bit.
Clearing out my cookies worked to authenticate me through MySpace. However, I followed up with another question about how Orkut authentication would work, since it doesn't seem to support 3-legged auth. Here was the response I received:
Orkut is interested in supporting
this, so you'll be able to allow users
to share their information with your
application "correctly" in the future.
The corresponding two-legged app would
need to forward the current viewer's
OpenSocial ID back to your server,
probably along with an authorization
token you generate yourself so that
you can link a user's session on orkut
with a session on your own server.
Honestly, it's probably not usable
enough to support a standalone login
system.
Essentially, no, Orkut really can't be hooked into an external app (at least, not yet) without resorting to the anti-pattern.
If anyone has any further information on this topic, please feel free to share!
The pattern is also mentioned here http://sites.google.com/site/oauthgoog/2leggedoauth/2opensocialrestapi
Essentially a lot of the mashups would want this feature :
A 3rd party site which DOES NOT have a
gadget wants to get the end-users
permission to access their data at the
social network, for example to
download their friend list, or to get
permission to post to their activity
stream