Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filters to authentication flow to allow external authentication #129

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

acafourek
Copy link

@acafourek acafourek commented Apr 25, 2021

Adds two filters:
graphql_jwt_auth_use_wp_authentication -- returns boolean to determine if should use WP authentication
graphql_jwt_auth_authenticate_user -- returns authenticated user or WP_Error

Also passes full query input along to Auth functions and filters to give SSO provider access to data. The modifications to the JWT plugin are fairly minimal and maintain backward compatibility.

We've been using this modification in order to support an SSO integration where users are linked to an external site to login and we determine authentication based on the validation of a code they return with.

I thought this might be useful to others, so I've made the changes to the core plugin as minimal as possible and you can also see below how we've implemented them in our own code. Using these new filters, we've written some code in our own plugin that adds some parameters to the loginUser mutation where we pass SSO information, then tap that outside service for authentication.

class demo_SSO {

     public function __construct() {
        
        //Add our own fields to login muation
        add_filter('graphql_input_fields', [$this,'custom_login_mutation_fields'],10,2);

        //Tell JWT Plugin if it should use WP authentication
        add_filter('graphql_jwt_auth_use_wp_authentication', [$this,'should_use_wp_authentication'],10,2);

        //Hook our own authenticator into the Login mutation
        add_filter('graphql_jwt_auth_authenticate_user', [$this,'authenticate_user_login_with_sso'],10,4);

        //register new input type
        $this->create_sso_inputType();
    }

    /** Determine if we should use WP auth or not **/

        public function should_use_wp_authentication($default,$fullInput){
            
            //allow devs to skip SSO for testing queries locally
            if((wp_get_environment_type() == "local") && (isset($fullInput['use_wp_auth']) && $fullInput['use_wp_auth'] == 1))
                return false;
        }

    /**
    * Define SSO input type
        * Creates new field type we can add to user login to specify SSO provider data
    **/
        public function create_sso_inputType(){
            if(!function_exists('register_graphql_input_type'))
                return;

            register_graphql_input_type(
                'ThirdPartyLoginInput',
                [
                    'description' => 'Login parameters to specify which third party authentication provider should be used to facilitate login.',
                    'fields'      => [
                        'provider'  => [
                            'type'        => 'String',
                            'description' => 'Name of the 3rd Party SSO Provider',
                        ],
                        'auth'  => [
                            'type'        => 'String',
                            'description' => 'Authentication string provided by OAuth flow that you would like to pass to the external authentication provider.',
                        ],
                    ]
                ]
            );
        }

    /**
     * Add custom fields to Login Mutation defined by JWT Plugin
    */
        public function custom_login_mutation_fields($input_fields, $type_name){
            if($type_name == "LoginInput"){
                $input_fields['third_party'] = [
                    'type' => 'ThirdPartyLoginInput',
                    'description' => "Information provided by a third party authentication flow."
                ];
                
               if((wp_get_environment_type() == "local")){
                     $input_fields['use_wp_auth'] = [
                         'type' => 'Boolean',
                         'description' => "Exempt authentication request from any SSO settings and use WP authentication."
                     ];
               }
            }
        
            return $input_fields;
        }

    /**
     * Use login mutaton data to validate based on authentication token
     */
        public function authenticate_user_login_with_sso($false,$username,$password,$fullInput){

            //make sure we have third party fields
            if(!is_array($fullInput) || !isset($fullInput['third_party']) || !is_array($fullInput['third_party']))
                return false;

            //make sure we have the right third party fields
            if(!isset($fullInput['third_party']['provider']) || !isset($fullInput['third_party']['provider']))
                return false;

            //make sure those fields say to use our SSO method
            if($fullInput['third_party']['provider'] !== "MyCustomSSOProvider" || !isset($fullInput['third_party']['auth']))
                return false;

            //returns WP_User or WP_Error
            $sso = (new MY_SSO_CLASS)->getUserByToken($fullInput['third_party']['auth']);

            if(!is_a($sso,'WP_User') && !is_wp_error($sso))
                $sso = new WP_ERROR('An unknown error occured during single sign on authentication');
            
            return $sso;
        }
}

So then our loginUser mutation looks like this:

mutation LoginUser($username: String!, $password: String!, $sso: ThirdPartyLoginInput) {
	login( input: {
        clientMutationId: "uniqueId",
        username: $username,
        password: $password,
        third_party: $sso
	} ) {
        ...someFields
    }
}
{
	"username": "[email protected]",
	"password": "p@ssword",
	"sso": {
		"provider": "MyCustomSSOProvider",
		"auth": "f962681c-89a8-49dd-ba51-8s5f5e91243"
	}
}

Adds two filters:
`graphql_jwt_auth_use_wp_authentication` -- returns boolean to determine if should use WP authentication
`graphql_jwt_auth_authenticate_user` -- returns authenticated user or WP_Error

Also passes full query input along to Auth functions and filters to give SSO provider access to data
@acafourek acafourek closed this Apr 25, 2021
@acafourek acafourek reopened this Apr 25, 2021
@2Cubed-ie
Copy link

Hi @acafourek, Would you have a working example of how this would work on a front end app. For example, how would I run this after I get a response from Google's SSO? Any info you have would be greatly appreciated.

@acafourek
Copy link
Author

Hi @acafourek, Would you have a working example of how this would work on a front end app. For example, how would I run this after I get a response from Google's SSO? Any info you have would be greatly appreciated.

My code here assumes you have some supporting WP plugin applying filters to the WPGraphql JWT plugin that intercepts the authentication flow, interprets data from the front end and tells the plugin if your extra level of authentication has been successful. So your FE app would only be involved so far as it's making the GQL query to LoginUser.

But you could certainly use the approach described above to support some Google SSO authentication flow, but you'd need to write your own WP Plugin to communicate with Google inside the filters I outline in the example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants