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.
- 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
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
-
RADIUS - https://github.com/dapphp/radius
-
LDAP - https://github.com/Adldap2/Adldap2 | http://adldap2.github.io/Adldap2/
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.
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.
https://github.com/scheb/two-factor-bundle
https://github.com/paragonie/multi_factor
Each external/primary authenticator will be responsible for authenticating the user everytime they requese a page after they log in.
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.
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.
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.
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"
}
}
]
}
}
}
{
"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.
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.
Name | Comment |
---|---|
auth_methods | Contains all of the available authentication methods |
account_auth_method | Associates an account with a authentication method |
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.
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?).
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.
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..