PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.3
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.3
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / vendor / wp-media / mcp-oauth / inc / Auth / JWT.php

JWT.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.3.3, at vendor/wp-media/mcp-oauth/inc/Auth/JWT.php

106 lines 3.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * JWT Helper — pure HS256 implementation with no external dependencies.
4 */
5
6 declare(strict_types=1);
7
8 namespace WPMedia\MCP\OAuth\Auth;
9
10 /**
11 * JWT encoder / decoder.
12 *
13 * Implements HS256 (HMAC-SHA256) signing. All methods are static so callers
14 * never need to instantiate the class.
15 */
16 class JWT {
17
18 /**
19 * Encode a payload as a signed JWT.
20 *
21 * @param array<string, mixed> $payload Claims to include in the token.
22 * @param string $secret HMAC signing secret.
23 * @return string Signed JWT string.
24 */
25 public static function encode( array $payload, string $secret ): string {
26 $header = self::base64url_encode(
27 (string) wp_json_encode(
28 [
29 'typ' => 'JWT',
30 'alg' => 'HS256',
31 ]
32 )
33 );
34 $body = self::base64url_encode( (string) wp_json_encode( $payload ) );
35 $sig = self::base64url_encode( hash_hmac( 'sha256', $header . '.' . $body, $secret, true ) );
36
37 return $header . '.' . $body . '.' . $sig;
38 }
39
40 /**
41 * Decode and verify a JWT.
42 *
43 * Returns null if the signature is invalid or (when $verify_expiry is true)
44 * the token has expired. Pass false for $verify_expiry when the caller
45 * needs to act on an expired token — e.g. the revocation endpoint, which
46 * must accept expired tokens per RFC 7009.
47 *
48 * @param string $token JWT string.
49 * @param string $secret HMAC signing secret.
50 * @param bool $verify_expiry Whether to reject tokens whose exp has passed.
51 * @return array<string, mixed>|null Decoded payload, or null on failure.
52 */
53 public static function decode( string $token, string $secret, bool $verify_expiry = true ): ?array {
54 $parts = explode( '.', $token );
55 if ( 3 !== count( $parts ) ) {
56 return null;
57 }
58
59 list( $header, $body, $signature ) = $parts;
60
61 // Pin the algorithm. We only ever issue and verify HS256; rejecting any
62 // other alg (notably 'none') defends against algorithm-substitution
63 // attacks explicitly rather than relying on the HMAC comparison alone.
64 $header_data = json_decode( self::base64url_decode( $header ), true );
65 if ( ! is_array( $header_data ) || 'HS256' !== ( $header_data['alg'] ?? '' ) ) {
66 return null;
67 }
68
69 $expected_sig = self::base64url_encode( hash_hmac( 'sha256', $header . '.' . $body, $secret, true ) );
70 if ( ! hash_equals( $expected_sig, $signature ) ) {
71 return null;
72 }
73
74 $payload = json_decode( self::base64url_decode( $body ), true );
75 if ( ! is_array( $payload ) ) {
76 return null;
77 }
78
79 if ( $verify_expiry && isset( $payload['exp'] ) && (int) $payload['exp'] < time() ) {
80 return null;
81 }
82
83 return $payload;
84 }
85
86 /**
87 * Base64URL-encode a binary string.
88 *
89 * @param string $data Raw bytes to encode.
90 * @return string URL-safe base64 without padding.
91 */
92 public static function base64url_encode( string $data ): string {
93 return rtrim( strtr( base64_encode( $data ), '+/', '-_' ), '=' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
94 }
95
96 /**
97 * Base64URL-decode a string.
98 *
99 * @param string $data URL-safe base64 string.
100 * @return string Decoded binary string.
101 */
102 private static function base64url_decode( string $data ): string {
103 return (string) base64_decode( strtr( $data, '-_', '+/' ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
104 }
105 }
106