Skip to content

Instantly share code, notes, and snippets.

@archey347
Last active May 26, 2019 14:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save archey347/47b18e1f2618755488d09c0b6a19e99e to your computer and use it in GitHub Desktop.
Save archey347/47b18e1f2618755488d09c0b6a19e99e to your computer and use it in GitHub Desktop.
UserFrosting Login Feature Suggestion

UserFrosting Login Feature Suggestion

This plan outlines a possible method of integrating social login, 2 factor authentication, and also the option for a developer to introduce their own authentication methods through their own sprinkles.

For every authentication method, there will be an authenticator class, which will have standard methods depending on what type of authenticator it is (see below). Each authenticator will be enabled in the config file. This is also where any application credentials that are needed by the authenticator will also be stored.

The presence of it in the configuration will then allow UserFrosting to "register" the authenticator class in a sub container called authenticators. They have been set up in the config so that multiple instances of the same authenticator class can be used, such as if a person had two standard oath2 sites they wanted to use.

The different authenticator types will be split up into sub-containers again depending on what type it is.

Authenticator Types

  • Primary - Uses standard username/password form
  • External - Requires user input/redirect through another website
  • Secondary - Any 2nd Factor Authentication
  • API - Used for authenticating any API endpoints

Primary

This mode will use the standard username/password form. Each plugin will have a priority, so that the plugin with the highest priority will be tried first, and the one with the second highest will be tried second and so on. The priority will probably be set by the order in which they come in the config file.

This allows for other login sources to be used, such as LDAP or RADIUS.

This plugin will be based on the PrimaryAuthenticator class

Libraries to use

External

This sort of plugin requires the user to login through another website or a popup, typically through OAuth or OpenID

This plugin will be based on the ExternalAuthenticator class

All external authenticators will have two options:

  • Log in as - allows users to log in directly using their external account.
  • Assoiciate account - allows a user to associate a UF account with an external account, for example if you wanted to import photos from facebook.

There would have to be checks in place to prevent a person from linking the same log in as account with the same assoicative account if both options are enabled.

Libraries to use

Secondary

This is any 2nd Factor authentication. If 2nd Factor authentication is required by an account, when a user logs in they can select any one of the methods that they have set up. They could include:

  • Time Based Code Generator
  • A list of 'Backup' codes
  • A text message with a code.
  • U2F token (if possible)

This plugin will be based on the SecondaryAuthenticator class

There will also have to be an optional "Remember this device" feature implemented here so that if a user logs in again using the same computer they won't have to go through the two factor authentication again. There will then be another bit in the user's account settings that would allow them to see what devices they have allowed. Basic device details could be attained by using this library or a js scrpit could be setup to collect some basic information and return it to the server.

Potential Libraries

https://github.com/scheb/two-factor-bundle

https://github.com/paragonie/multi_factor

Session authentication

Each external/primary authenticator will be responsible for authenticating the user everytime they requese a page after they log in.

Authenticator Class

Each authenticator class will have standard methods, depending on what type it is.

The authenticator classes will also have to manage changing account details etc. It may be nescerary to have seperate account handlers as well, so that if a person wants to use their own oauth server, they can implement it with their own api. An avatar provider class may also have to be implemented with this. (Ref #620)

For the external authenticator class, a method will be needed to get a path for a twig template with a login button (e.g. a Login with Facebook button), and for the secondary authenticator then a method will be needed to render an additional login page.

A standard HybridAuthAuthenticator might be useful which can be extended for each provider supported by HybridAuth.

Configuration

The different plugins can be enabled or disabled by the configuration, along with any other additional options. As UF uses different configuration files for development and production, it would allow for a developer to more easily use "sandbox credentials" whilst developing.

Options

Config Name Type Description
slug String Name in the container and in the database
class_name String The full path to the class to use
options Array Any additional options needed by the specific authenticator

Any credentials will have to be put into the .env file and then loaded into the config using the getenv() function.

Format

The configuration will look something like this:

{
    "authenticators" : {
            "plugin_type" : [
                {
                    "__comment" : "The item defined first will have the highest priority"
                },
                {
                    "slug" : "slug_name",
                    "class_name" : "UserFrosting\\Sprinkle\\ExampleSprinkle\\Authenticators\\PluginType\\AuthenticatorClassName",
                    "options" : {
                        "option1" : "Additional Settings Go Here"
                    }
                }
            ]
        }
    }
}

Example

{
    "authenticators" : {
            "primary" : [
                {
                    "slug" : "database",
                    "class_name" : "DatabaseAuthenticator"
                },
                {
                    "slug" : "ldap",
                    "class_name" : "UserFrosting\\Sprinkle\\ExampleSprinkle\\Authenticator\\Primary\\LDAPAuthenticator",
                    "options" : {
                        "account_suffix" : "@example.com",
                        "domain_controllers" : [
                            "dc1.example.com",
                            "dc2.example.com"
                        ],
                        "admin_username" : "",
                        "admin_password" : ""
                    }
                }
            ],
            "external" : [
                {
                    "slug" : "github",
                    "class_name" : "UserFrosting\\Sprinkle\\ExampleSprinkle\\Authenticator\\External\\GithubAuthenticator",
                    "options" : {
                        "app_id" : "A App ID",
                        "app_secret" : "Some secret....."
                    }
                }
            ],
            "secondary" : [
                {
                    "slug" : "google_time_code",
                    "class_name" : "UserFrosting\\Sprinkle\\ExampleSprinkle\\Authenticator\\Secondary\\GoogleTimeCodeAuthenticator",
                    "options" : {
                        "salt" : "AKHD3425..."
                    }
                }
            ]
        }
    }
}

If there is no primary or external authentication configuration set, then it will use a "built-in" configuration for using the passwords in the database. It might be worth making the default uf authentication method mandatory after all of the other primary authenticators have been tried.

Profile pictures

Both primary and secondar authenticator will have to provide a profile picture for the user. If one is not found, then gravatar can be used.

Database

Name Comment
auth_methods Contains all of the available authentication methods
account_auth_method Associates an account with a authentication method

auth_methods

Name Type Comments
id integer Auto Increment, Primary Key
slug String

The config will be used to generate all of the rows in the auth_method table, maybe through another bakery command which also runs on setup.

account_auth_method

Name Type Comments
id Int Auto Increment, Primary Key
account_id Int Links to accounts.id
auth_method_id Int Links to auth_methods.id
options TEXT Any credentials needed - In JSON format

Using JSON in a database is a bit crude - I don't know whether it would be worth to go the extra mile and do like a account_auth_method_options table instead.

Note that as Userfrostings internal username and password database would be classed as a primary authenticator, the storage location of the password would move into the account_auth_method table. (Should the username/email move as well?).

User handling

For both external and internal authenticators, there must be some method of uniquely identifying a user from each identity provider, but it is possible for a user from one identity provider to have the same id as a different user from a different identity provider.

If an external user logs in for the first time, a standard userfrosting account should be created, but instead of having UF's primary authenticator, they would have the external authenticator. If that user were to then login again, a query would be done on the account_auth_method to find the UF account using the external account's id.

Technical Design

Items marked with a * won't be developed in the first version.

Interfaces

Ref: https://github.com/laravel/framework/blob/5.3/src/Illuminate/Auth/SessionGuard.php https://github.com/userfrosting/UserFrosting/blob/master/app/sprinkles/account/src/Authenticate/Authenticator.php

Primary Authenticator

External Authenticator

Secondary Authenticator

Database Tables

remembered_devices *

Stores the remembered devices for two factor authentication

Field Name Data Type Description
device_id int
account_id int
device_secret string A token to help identify it
last_login timestamp the last time this device was used to access this account

account_authentication_methods

Stores all of the configuration settings for each authentication method for each user.

Field Name Data Type Description
@amosfolz
Copy link

I think if we were to integrate these two into UF it would cover a majority of needs...
https://hybridauth.github.io/providers.html
https://simplesamlphp.org/docs/stable/simplesamlphp-idp

@archey347
Copy link
Author

archey347 commented May 25, 2019

I don't think SAML is more useful if you wanted to have multiple services use the same identity provider considering that it says that the service provider is separate from the identity provider part, but I might be wrong as I've no idea what it actually is.

@amosfolz
Copy link

The section "A SAML Example" in this article provides a pretty good overview.

UserFrosting would be the Service Provider and would allow authentication via all the services listed here

@archey347
Copy link
Author

I think the issue with that is the SAML IdP would have to be a separate part of UserFrosting. Looking at the page below it looks as if the user gets sent to the IdP to enter in their username and password, but I think simpleSAMLphp would be a good candidate for an external authenticator.

https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-simplesamlphp-for-saml-authentication-on-ubuntu-16-04

@amosfolz
Copy link

I think if we were to integrate these two into UF it would cover a majority of needs...
https://hybridauth.github.io/providers.html
https://simplesamlphp.org/docs/stable/simplesamlphp-idp

I might have been a little ambiguous with this statement...

If the goal is to make this extensible so that custom authentication methods can be added I think it would still be beneficial to have some built-in default options available (as is already planned for hybridauth).

I believe it would really appeal to the enterprise community if UF came with native SAML support. I would agree it falls under the external authenticator category and we may find it would be better suited for implementation via a community sprinkle rather than with the core.

We should think about list-able project goals we can use to help prevent project creep. Also, would you be alright with me doing it, or would you like to migrate the information you already have here into a new branch of forked userfrosting? So we can both have edit access and more easily track changes, etc..

@archey347
Copy link
Author

I was intending on allowing for custom authentication methods to allow people to use it if their organization uses a proprietary system, or if a completely new authentication protocol is created in the future.

A definitive plan would be useful because I keep thinking of different features - I need to figure out which ones are actually needed.

I've made a new fork of UserFrosting, and I've added you as a collaborator, so you should have push access. I've moved all of the information into the wiki bit of the fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment