header ) || ! property_exists( $parts->header, 'typ' ) || ! property_exists( $parts->header, 'alg' ) || self::$type !== $parts->header->typ || self::$algorithm !== $parts->header->alg ) { return false; } /** * Check if token is expired. */ if ( ! property_exists( $parts->payload, 'exp' ) || time() > (int) $parts->payload->exp ) { return false; } /** * Check if the token is based on our secret. */ $encoded_regenerated_signature = self::to_base_64_url( self::generate_signature( $parts->header_encoded . '.' . $parts->payload_encoded, $secret ) ); return hash_equals( $encoded_regenerated_signature, $parts->signature_encoded ); } /** * Returns the decoded/encoded header, payload and signature from a token string. * * @param string $token Full token string. * * @return object */ public static function get_parts( string $token ) { $parts = explode( '.', $token ); return (object) array( 'header' => json_decode( self::from_base_64_url( $parts[0] ) ), 'header_encoded' => $parts[0], 'payload' => json_decode( self::from_base_64_url( $parts[1] ) ), 'payload_encoded' => $parts[1], 'signature' => self::from_base_64_url( $parts[2] ), 'signature_encoded' => $parts[2], ); } /** * Generates the json formatted header for our HS256 JWT token. * * @return string|bool */ private static function generate_header() { return wp_json_encode( array( 'alg' => self::$algorithm, 'typ' => self::$type, ) ); } /** * Generates a sha256 signature for the provided string using the provided secret. * * @param string $string Header + Payload token substring. * @param string $secret The secret used to generate the signature. * * @return false|string */ private static function generate_signature( string $string, string $secret ) { return hash_hmac( 'sha256', $string, $secret, true ); } /** * Generates the payload in json formatted string. * * @param array $payload Payload data. * * @return string|bool */ private static function generate_payload( array $payload ) { return wp_json_encode( array_merge( $payload, [ 'iat' => time() ] ) ); } /** * Encodes a string to url safe base64. * * @param string $string The string to be encoded. * * @return string */ private static function to_base_64_url( string $string ) { return str_replace( array( '+', '/', '=' ), array( '-', '_', '' ), base64_encode( $string ) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode ); } /** * Decodes a string encoded using url safe base64, supporting auto padding. * * @param string $string the string to be decoded. * * @return string */ private static function from_base_64_url( string $string ) { /** * Add padding to base64 strings which require it. Some base64 URL strings * which are decoded will have missing padding which is represented by the * equals sign. */ if ( strlen( $string ) % 4 !== 0 ) { return self::from_base_64_url( $string . '=' ); } return base64_decode( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode str_replace( array( '-', '_' ), array( '+', '/' ), $string ) ); } }