Jump to content

User authentication by GET parameter?


FlorianA
 Share

Recommended Posts

Hi,

I would like to provide an ICS calendar URL for registered users. However, it should be possible to load this URL without the need to login before, since this would be difficult for many calendar apps. That's why I would like to generate an individual URL for each user which contains a kind of authentication token. An implementation could look like this:

  • The token is an MD5 hash of the user name, his encrypted password and maybe something else.
  • Both user name and token are added to the URL as GET params.
  • When calling the template, a token is calculated from user name parameter and compared to the token parameter. If both are identical, the user will be handled as authenticated.

What's the easiest way to implement somethin like this in PW? The exact solution above seems not to be possible, as AFAIK the API doesn't provide access to the encrypted password. But I'm sure there's any existing solution for this in PW or any module  ?

Link to comment
Share on other sites

2 hours ago, FlorianA said:

What's the easiest way to implement somethin like this in PW? The exact solution above seems not to be possible, as AFAIK the API doesn't provide access to the encrypted password. But I'm sure there's any existing solution for this in PW or any module  ?

Actually the API provides access to the hashed password.

If this password hash (the one you use for your secure URLs) needs to remain constant, I guess that's as good approach as any – mash some non-public variables together, generate a hash, and then check if the user has provided the correct hash. If you're looking for a ready-made solution I'm not aware of one, but on the other hand this sounds like something you could put together in a few lines of code, so not really a massive problem.

The specific implementation depends on how your site is built, but technically you just need to add a bit of code somewhere before any markup is rendered – check a GET param from $input->get->token_var_name, and then compare that to a correct token (or one stored in user details – more about that in the list below). I would probably use URL segments here, so that first segment contains the username and second one the token, though.

If there's a match, do something (display page content), otherwise throw a Wire404Exception (preferable over displaying a clear message such as "wrong token"; you don't want third parties to know even if they got the username right.)

Some additional steps to consider, mainly for security reasons:

  • Instead of calculating the token on the go, you could generate it automatically for every user, and store it in a custom field added to the user template. This way you can also include random data in the original hash, which means that it cannot, under any circumstances, be used to uncover (hashed) passwords or any other type of sensitive data.
    • Note: this would still mean that the token is stored in the database as-is, which would become a problem in case this information ever leaks. Technically the safest thing to do might be to send the private token to the user, and never store it as-is, but rather convert it to a hash using a salt and then store those locally ?
  • Add some sort of brute force protection to the page, i.e. after a few incorrect attempts block the IP for a while.
  • Preferably refresh (recreate) tokens after a set amount of time. This may not be particularly important, but personally I believe it's a good practice in these sorts of situations.
  • Provide a way for users to change (reset) this key, in case there's a chance that someone else has gained access to it. Preferably this new key should also be randomly generated, so that users cannot manually type in a weak key.
  • Use username or some other value in the URL itself, so that keys become much harder to guess (via brute force attack) – but also make sure that third parties can't use these URLs to collect a list of existing usernames ?

... although the security level of course depends on the type of information you're providing via this calendar. If it's not particularly sensitive data, then it won't be such a big deal, but if there's a chance that it may, for an example, contain personal data, then definitely take every possible precaution.

  • Like 4
Link to comment
Share on other sites

13 hours ago, teppo said:

Instead of calculating the token on the go, you could generate it automatically for every user, and store it in a custom field added to the user template.

As an alternative, you could store it on the file system, outside the web root. Perhaps like user-id_token.txt or similar.

  • Like 1
Link to comment
Share on other sites

14 hours ago, teppo said:

Actually the API provides access to the hashed password.

Hi all, thank you for your answers. In the meantime, I also found out how to access the hashed password, though unfortunately the documentation is hidden very well in a comment of the User class source:

Quote

* Additional notes regarding the $user->pass property: 
* Note that when getting, this returns a hashed version of the password, so it is not typically useful to get this property. 
* However, it is useful to set this property if you want to change the password. When you change a password, it is assumed 
* to be the non-hashed/non-encrypted version. ProcessWire will hash it automatically when the user is saved.

I still think, using the password hash is a better solution for my calendar than generating a random token:

  • There is no need to implement any storage logic.
  • As soon as the user changes the password, the token will be invalidated automatically.
  • I won't do a force login from the token but only will grant the user's access to some restricted fields in the calendar data.
  • As the security level of this data is not too high, this simple solution should be enough.
Link to comment
Share on other sites

By the way, what is the easiest way in PW to show a page the same way another user would see it, if he was logged in (including properties only visible by the other user)? Can I use checkAccess=0 in a $page->get() query or something else?

Link to comment
Share on other sites

I think a debugger won't help me. I'd just like to retrieve a page property although the corresponding field is not visible by the current user - because it would be visible by another user who has just been "authenticated" by a token.

Link to comment
Share on other sites

13 hours ago, FlorianA said:

I think a debugger won't help me. I'd just like to retrieve a page property although the corresponding field is not visible by the current user - because it would be visible by another user who has just been "authenticated" by a token.

Tracy debugger has many tools to help in development. One of them is the user-switcher, which exactly does what you asked for!

There are also other modules out there that provide this user switcher functionality. If I do remember right, @Pete has build and released one. (search in the modules directory for it)
My ALIF module once came up with this, but in the meantime I have stripped it out, as there are now other solutions for this, so that I don't have to maintain to much functionality with my modules. :)

  • Like 1
Link to comment
Share on other sites

On 8/4/2019 at 8:44 PM, FlorianA said:

I think a debugger won't help me. I'd just like to retrieve a page property although the corresponding field is not visible by the current user - because it would be visible by another user who has just been "authenticated" by a token.

OK, I've found it out myself now. The trick is to make the field accessible by API. Then you can simply write something like:

function getRestrictedProperty($page, $fieldname, $user)
{
    $field = $page->getFields()->get($fieldname);
    if ($field == null)
        return '';
    return $field->viewable($page, $user) ? $page->get($fieldname) : '';
}

 

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...