Basic skeleton
Here is a basic skeleton for an OAuth-based login handler:
namespace IPS\Login; class _Example extends LoginAbstract { /** * @brief Icon */ public static $icon = 'lock'; /** * Get Form * * @param \IPS\Http\Url $url The URL for the login page * @param bool $ucp If this is being done from the User CP * @return string */ public function loginForm( $url, $ucp=FALSE ) { $redirectUrl = \IPS\Http\Url::internal( 'applications/core/interface/example/auth.php', 'none' ); $oauthUrl = \IPS\Http\Url::external( "https://www.example.com/oauth" )->setQueryString( array( 'client_id' => 'xxx', 'redirect_uri' => (string) $redirectUrl ) ); return "<a href='{$oauthUrl}'>Login</a>"; } /** * Authenticate * * @param string $url The URL for the login page * @param \IPS\Member $member If we want to integrate this login method with an existing member, provide the member object * @return \IPS\Member * @throws \IPS\Login\Exception */ public function authenticate( $url, $member=NULL ) { /* Get user details from service */ $userData = \IPS\Http\Url::external( "https://www.example.com/userData" )->setQueryString( 'token', \IPS\Request::i()->token )->request()->get()->decodeJson(); /* Get or create member */ if ( $member === NULL ) { /* Try to find member */ $member = \IPS\Member::load( $userData['id'], 'my_custom_id' ); /* If we don't have one, create one */ if ( !$member->member_id ) { /* If a member already exists with this email, prompt them to merge */ $existingEmail = \IPS\Member::load( $userData['email'], 'email' ); if ( $existingEmail->member_id ) { $exception = new \IPS\Login\Exception( 'generic_error', \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT ); $exception->handler = 'Example'; $exception->member = $existingEmail; $exception->details = \IPS\Request::i()->token; throw $exception; } /* Create member */ $member = new \IPS\Member; $member->member_group_id = \IPS\Settings::i()->member_group; /* Is a user doesn't exist with this username, set it (if it does, the user will automatically be prompted) */ $existingUsername = \IPS\Member::load( $userData['name'], 'name' ); if ( !$existingUsername->member_id ) { $member->name = $userData['name']; } /* Set validating if necessary */ if ( \IPS\Settings::i()->reg_auth_type == 'admin' or \IPS\Settings::i()->reg_auth_type == 'admin_user' ) { $member->members_bitoptions['validating'] = TRUE; } } } /* Set service ID */ $member->my_custom_id = $userData['id']; $member->save(); /* Return */ return $member; } /** * Link Account * * @param \IPS\Member $member The member * @param mixed $details Details as they were passed to the exception thrown in authenticate() * @return void */ public static function link( \IPS\Member $member, $details ) { $userData = \IPS\Http\Url::external( "https://www.example.com/userData" )->setQueryString( 'token', $details )->request()->get()->decodeJson(); $member->my_custom_id = $userData['id']; $member->save(); } /** * ACP Settings Form * * @param string $url URL to redirect user to after successful submission * @return array List of settings to save - settings will be stored to core_login_handlers.login_settings DB field * @code return array( 'savekey' => new \IPS\Helpers\Form\[Type]( ... ), ... ); * @endcode */ public function acpForm() { return array(); } /** * Can a member change their email/password with this login handler? * * @param string $type 'email' or 'password' * @param \IPS\Member $member The member * @return bool */ public function canChange( $type, \IPS\Member $member ) { return FALSE; } }
The $icon parameter should be the name of a FontAwesome icon classname (without the fa- prefix) which is used on some login screens.
The loginForm() method is used to display the HTML you need for the form. For an OAuth-based handler, this will usually just return the appropriate login button. You can alternatively return an \IPS\Helpers\Form object.
The authenticate() method is where the bulk of your login code will go. If your loginForm() method returns an \IPS\Helpers\Form object it will be passed an array of values from that form (just like standard login handlers). If your loginForm() method returns raw HTML, it is your responsibility to ultimately redirect the user back to the same URL that was passed as $url to loginForm with the "loginProcess" set to the key for your login handler. Most OAuth providers do this with a gateway script in the /interface directory.
Your authenticate() method needs to return an \IPS\Member object or throw an \IPS\Login\Exception object, as follows:
// Something went wrong with the login handler which wasn't the user's fault. throw new \IPS\Login\Exception( "Login Failed.", \IPS\Login\Exception::INTERNAL_ERROR ); // The password the user provided was incorrect. throw new \IPS\Login\Exception( "Login Failed.", \IPS\Login\Exception::BAD_PASSWORD ); // The username or email address the user provided did not match any account. throw new \IPS\Login\Exception( "Login Failed.", \IPS\Login\Exception::NO_ACCOUNT ); // The username or email address matches an existing account but which has not been used by this // login handler before and an account merge is required (see below) throw new \IPS\Login\Exception( "Login Failed.", \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT );
The acpForm(), link() and changeSettings() methods are described in the following sections of this guide.
Report Document