Jump to content

Developer Documentation

Creating other login handlers (such as OAuth)

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


×
×
  • Create New...