Skip to content

Commit f8c648e

Browse files
authored
Merge pull request #1 from firebase/master
同步更新
2 parents bac0422 + 474047d commit f8c648e

17 files changed

+643
-124
lines changed

.gitattributes

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@
55
/.gitignore export-ignore
66
/.travis.yml export-ignore
77
/phpunit.xml.dist export-ignore
8-
/run-tests.sh export-ignore

.travis.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
11
language: php
22

3+
branches:
4+
- only: [master]
5+
36
php:
4-
- 5.4
5-
- 5.5
67
- 5.6
78
- 7.0
89
- 7.1
910
- 7.2
11+
- 7.3
12+
- 7.4
1013

1114
matrix:
1215
include:
1316
- php: 5.3
1417
dist: precise
18+
- php: 5.4
19+
dist: trusty
20+
- php: 5.5
21+
dist: trusty
22+
- name: "Check Style"
23+
php: "7.4"
24+
env: RUN_CS_FIXER=true
1525

1626
sudo: false
1727

1828
before_script: composer install
19-
script: phpunit
29+
script:
30+
- if [ "${RUN_CS_FIXER}" = "true" ]; then
31+
composer require friendsofphp/php-cs-fixer &&
32+
vendor/bin/php-cs-fixer fix --diff --dry-run . &&
33+
vendor/bin/php-cs-fixer fix --rules=native_function_invocation --allow-risky=yes --diff src;
34+
else
35+
vendor/bin/phpunit;
36+
fi

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Example
2323
use \Firebase\JWT\JWT;
2424

2525
$key = "example_key";
26-
$token = array(
26+
$payload = array(
2727
"iss" => "http://example.org",
2828
"aud" => "http://example.com",
2929
"iat" => 1356999524,
@@ -36,7 +36,7 @@ $token = array(
3636
* https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
3737
* for a list of spec-compliant algorithms.
3838
*/
39-
$jwt = JWT::encode($token, $key);
39+
$jwt = JWT::encode($payload, $key);
4040
$decoded = JWT::decode($jwt, $key, array('HS256'));
4141

4242
print_r($decoded);
@@ -93,14 +93,14 @@ ehde/zUxo6UvS7UrBQIDAQAB
9393
-----END PUBLIC KEY-----
9494
EOD;
9595

96-
$token = array(
96+
$payload = array(
9797
"iss" => "example.org",
9898
"aud" => "example.com",
9999
"iat" => 1356999524,
100100
"nbf" => 1357000000
101101
);
102102

103-
$jwt = JWT::encode($token, $privateKey, 'RS256');
103+
$jwt = JWT::encode($payload, $privateKey, 'RS256');
104104
echo "Encode:\n" . print_r($jwt, true) . "\n";
105105

106106
$decoded = JWT::decode($jwt, $publicKey, array('RS256'));
@@ -115,6 +115,19 @@ echo "Decode:\n" . print_r($decoded_array, true) . "\n";
115115
?>
116116
```
117117

118+
Using JWKs
119+
----------
120+
121+
```php
122+
// Set of keys. The "keys" key is required. For example, the JSON response to
123+
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
124+
$jwks = ['keys' => []];
125+
126+
// JWK::parseKeySet($jwks) returns an associative array of **kid** to private
127+
// key. Pass this as the second parameter to JWT::decode.
128+
JWT::decode($payload, JWK::parseKeySet($jwks), $supportedAlgorithm);
129+
```
130+
118131
Changelog
119132
---------
120133

composer.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
"name": "firebase/php-jwt",
33
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
44
"homepage": "https://github.com/firebase/php-jwt",
5+
"keywords": [
6+
"php",
7+
"jwt"
8+
],
59
"authors": [
610
{
711
"name": "Neuman Vong",
@@ -24,6 +28,6 @@
2428
}
2529
},
2630
"require-dev": {
27-
"phpunit/phpunit": " 4.8.35"
31+
"phpunit/phpunit": ">=4.8 <=9"
2832
}
2933
}

run-tests.sh

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/BeforeValidException.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33

44
class BeforeValidException extends \UnexpectedValueException
55
{
6-
76
}

src/ExpiredException.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33

44
class ExpiredException extends \UnexpectedValueException
55
{
6-
76
}

src/JWK.php

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
3+
namespace Firebase\JWT;
4+
5+
use DomainException;
6+
use InvalidArgumentException;
7+
use UnexpectedValueException;
8+
9+
/**
10+
* JSON Web Key implementation, based on this spec:
11+
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
12+
*
13+
* PHP version 5
14+
*
15+
* @category Authentication
16+
* @package Authentication_JWT
17+
* @author Bui Sy Nguyen <[email protected]>
18+
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
19+
* @link https://github.com/firebase/php-jwt
20+
*/
21+
class JWK
22+
{
23+
/**
24+
* Parse a set of JWK keys
25+
*
26+
* @param array $jwks The JSON Web Key Set as an associative array
27+
*
28+
* @return array An associative array that represents the set of keys
29+
*
30+
* @throws InvalidArgumentException Provided JWK Set is empty
31+
* @throws UnexpectedValueException Provided JWK Set was invalid
32+
* @throws DomainException OpenSSL failure
33+
*
34+
* @uses parseKey
35+
*/
36+
public static function parseKeySet(array $jwks)
37+
{
38+
$keys = array();
39+
40+
if (!isset($jwks['keys'])) {
41+
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
42+
}
43+
if (empty($jwks['keys'])) {
44+
throw new InvalidArgumentException('JWK Set did not contain any keys');
45+
}
46+
47+
foreach ($jwks['keys'] as $k => $v) {
48+
$kid = isset($v['kid']) ? $v['kid'] : $k;
49+
if ($key = self::parseKey($v)) {
50+
$keys[$kid] = $key;
51+
}
52+
}
53+
54+
if (0 === \count($keys)) {
55+
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
56+
}
57+
58+
return $keys;
59+
}
60+
61+
/**
62+
* Parse a JWK key
63+
*
64+
* @param array $jwk An individual JWK
65+
*
66+
* @return resource|array An associative array that represents the key
67+
*
68+
* @throws InvalidArgumentException Provided JWK is empty
69+
* @throws UnexpectedValueException Provided JWK was invalid
70+
* @throws DomainException OpenSSL failure
71+
*
72+
* @uses createPemFromModulusAndExponent
73+
*/
74+
private static function parseKey(array $jwk)
75+
{
76+
if (empty($jwk)) {
77+
throw new InvalidArgumentException('JWK must not be empty');
78+
}
79+
if (!isset($jwk['kty'])) {
80+
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
81+
}
82+
83+
switch ($jwk['kty']) {
84+
case 'RSA':
85+
if (\array_key_exists('d', $jwk)) {
86+
throw new UnexpectedValueException('RSA private keys are not supported');
87+
}
88+
if (!isset($jwk['n']) || !isset($jwk['e'])) {
89+
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
90+
}
91+
92+
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
93+
$publicKey = \openssl_pkey_get_public($pem);
94+
if (false === $publicKey) {
95+
throw new DomainException(
96+
'OpenSSL error: ' . \openssl_error_string()
97+
);
98+
}
99+
return $publicKey;
100+
default:
101+
// Currently only RSA is supported
102+
break;
103+
}
104+
}
105+
106+
/**
107+
* Create a public key represented in PEM format from RSA modulus and exponent information
108+
*
109+
* @param string $n The RSA modulus encoded in Base64
110+
* @param string $e The RSA exponent encoded in Base64
111+
*
112+
* @return string The RSA public key represented in PEM format
113+
*
114+
* @uses encodeLength
115+
*/
116+
private static function createPemFromModulusAndExponent($n, $e)
117+
{
118+
$modulus = JWT::urlsafeB64Decode($n);
119+
$publicExponent = JWT::urlsafeB64Decode($e);
120+
121+
$components = array(
122+
'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
123+
'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
124+
);
125+
126+
$rsaPublicKey = \pack(
127+
'Ca*a*a*',
128+
48,
129+
self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
130+
$components['modulus'],
131+
$components['publicExponent']
132+
);
133+
134+
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
135+
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
136+
$rsaPublicKey = \chr(0) . $rsaPublicKey;
137+
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
138+
139+
$rsaPublicKey = \pack(
140+
'Ca*a*',
141+
48,
142+
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
143+
$rsaOID . $rsaPublicKey
144+
);
145+
146+
$rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
147+
\chunk_split(\base64_encode($rsaPublicKey), 64) .
148+
'-----END PUBLIC KEY-----';
149+
150+
return $rsaPublicKey;
151+
}
152+
153+
/**
154+
* DER-encode the length
155+
*
156+
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
157+
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
158+
*
159+
* @param int $length
160+
* @return string
161+
*/
162+
private static function encodeLength($length)
163+
{
164+
if ($length <= 0x7F) {
165+
return \chr($length);
166+
}
167+
168+
$temp = \ltrim(\pack('N', $length), \chr(0));
169+
170+
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
171+
}
172+
}

0 commit comments

Comments
 (0)