$state, 'code_challenge' => $challenge, 'redirect_uri' => admin_url( 'admin-post.php?action=aivastark_callback' ), 'site_url' => home_url(), ) ); // External redirect (dashboard origin), so wp_redirect rather than the // host-restricted wp_safe_redirect. wp_redirect( $authorize_url ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect exit; } /** * Finish the pairing: validate state, exchange the code, persist the token. */ public static function callback() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'aivastark-widget' ) ); } $home = admin_url( 'admin.php?page=aivastark' ); // User denied, or the dashboard returned an error. if ( isset( $_GET['error'] ) ) { self::redirect_with( $home, 'denied' ); } $code = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : ''; $state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( $_GET['state'] ) ) : ''; if ( '' === $code || '' === $state ) { self::redirect_with( $home, 'missing' ); } // CSRF: the state must match a verifier we issued (and not yet consumed). $transient_key = self::STATE_TRANSIENT_PREFIX . $state; $verifier = get_transient( $transient_key ); delete_transient( $transient_key ); // single-use if ( ! $verifier ) { self::redirect_with( $home, 'state' ); } $result = Aivastark_Widget_Api::exchange( $code, $verifier, home_url() ); if ( is_wp_error( $result ) ) { self::redirect_with( $home, 'exchange' ); } Aivastark_Widget_Options::save_pairing( $result, home_url() ); self::redirect_with( $home, '', 'connected' ); } /** * Revoke the token and clear local pairing state. */ public static function disconnect() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'aivastark-widget' ) ); } check_admin_referer( 'aivastark_disconnect' ); Aivastark_Widget_Api::revoke(); // best-effort; ignore failures Aivastark_Widget_Options::clear_pairing(); self::redirect_with( admin_url( 'admin.php?page=aivastark' ), '', 'disconnected' ); } /** * Redirect back to a plugin page with an error/notice query arg, then exit. */ private static function redirect_with( $base, $error = '', $notice = '' ) { $args = array(); if ( $error ) { $args['aivastark_error'] = $error; } if ( $notice ) { $args['aivastark_notice'] = $notice; } wp_safe_redirect( add_query_arg( $args, $base ) ); exit; } }