'code', 'client_id' => ADPRIVA_CLIENT_ID, 'redirect_uri' => self::redirect_uri(), 'code_challenge' => Adpriva_PKCE::challenge( $verifier ), 'code_challenge_method' => 'S256', 'site' => self::site_host(), 'scope' => 'summary', 'state' => $state, ) ); } /** * Exchange the returned code for tokens, after validating the state nonce. * * @param string $code Authorization code. * @param string $state State returned by the authorize redirect. * @return true|WP_Error */ public static function handle_callback( $code, $state ) { $expected = get_transient( self::STATE_TRANSIENT ); $verifier = get_transient( self::VERIFIER_TRANSIENT ); delete_transient( self::STATE_TRANSIENT ); delete_transient( self::VERIFIER_TRANSIENT ); if ( empty( $expected ) || empty( $verifier ) || ! hash_equals( (string) $expected, (string) $state ) ) { return new WP_Error( 'adpriva_state_mismatch', __( 'The connection could not be verified. Please try connecting again.', 'adpriva' ) ); } $response = wp_remote_post( Adpriva_Config::api_base() . '/oauth/token', array( 'timeout' => 15, 'body' => array( 'grant_type' => 'authorization_code', 'client_id' => ADPRIVA_CLIENT_ID, 'code' => $code, 'code_verifier' => $verifier, 'redirect_uri' => self::redirect_uri(), ), ) ); return self::store_token_response( $response ); } /** * Rotate the access token using the stored refresh token. * * @return bool True on success. */ public static function refresh() { $refresh = Adpriva_Tokens::refresh_token(); if ( '' === $refresh ) { return false; } $response = wp_remote_post( Adpriva_Config::api_base() . '/oauth/token', array( 'timeout' => 15, 'body' => array( 'grant_type' => 'refresh_token', 'client_id' => ADPRIVA_CLIENT_ID, 'refresh_token' => $refresh, ), ) ); return ! is_wp_error( self::store_token_response( $response ) ); } /** * Revoke the access token server-side and clear local storage. * * @return void */ public static function disconnect() { $token = Adpriva_Tokens::access_token(); if ( '' !== $token ) { wp_remote_post( Adpriva_Config::api_base() . '/oauth/revoke', array( 'timeout' => 10, 'body' => array( 'token' => $token ), ) ); } Adpriva_Tokens::clear(); } /** * Persist a successful token response. * * @param array|WP_Error $response Token endpoint response. * @return true|WP_Error */ private static function store_token_response( $response ) { if ( is_wp_error( $response ) ) { return $response; } $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( 200 !== (int) wp_remote_retrieve_response_code( $response ) || ! is_array( $body ) || empty( $body['access_token'] ) ) { return new WP_Error( 'adpriva_token_error', __( 'AdPriva did not return a valid token.', 'adpriva' ) ); } $domain = isset( $body['site']['domain'] ) ? (string) $body['site']['domain'] : ''; Adpriva_Tokens::save( array( 'access_token' => $body['access_token'], 'refresh_token' => isset( $body['refresh_token'] ) ? $body['refresh_token'] : '', 'expires_in' => isset( $body['expires_in'] ) ? (int) $body['expires_in'] : 3600, 'scope' => isset( $body['scope'] ) ? $body['scope'] : '', 'placement_key' => isset( $body['placement_key'] ) ? $body['placement_key'] : '', 'domain' => $domain, ) ); return true; } }