├── JWT.php
├── LICENSE
├── Makefile
├── package.php
├── package.xml
└── tests
├── Bootstrap.php
├── JWTTest.php
└── phpunit.xml
/JWT.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class JWT
12 | {
13 | /**
14 | * @param string $jwt The JWT
15 | * @param string|null $key The secret key
16 | * @param bool $verify Don't skip verification process
17 | *
18 | * @return object The JWT's payload as a PHP object
19 | */
20 | public static function decode($jwt, $key = null, $verify = true)
21 | {
22 | $tks = explode('.', $jwt);
23 | if (count($tks) != 3) {
24 | throw new UnexpectedValueException('Wrong number of segments');
25 | }
26 | list($headb64, $payloadb64, $cryptob64) = $tks;
27 | if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))
28 | ) {
29 | throw new UnexpectedValueException('Invalid segment encoding');
30 | }
31 | if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($payloadb64))
32 | ) {
33 | throw new UnexpectedValueException('Invalid segment encoding');
34 | }
35 | $sig = JWT::urlsafeB64Decode($cryptob64);
36 | if ($verify) {
37 | if (empty($header->alg)) {
38 | throw new DomainException('Empty algorithm');
39 | }
40 | if ($sig != JWT::sign("$headb64.$payloadb64", $key, $header->alg)) {
41 | throw new UnexpectedValueException('Signature verification failed');
42 | }
43 | }
44 | return $payload;
45 | }
46 |
47 | /**
48 | * @param object|array $payload PHP object or array
49 | * @param string $key The secret key
50 | * @param string $algo The signing algorithm
51 | *
52 | * @return string A JWT
53 | */
54 | public static function encode($payload, $key, $algo = 'HS256')
55 | {
56 | $header = array('typ' => 'JWT', 'alg' => $algo);
57 |
58 | $segments = array();
59 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header));
60 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload));
61 | $signing_input = implode('.', $segments);
62 |
63 | $signature = JWT::sign($signing_input, $key, $algo);
64 | $segments[] = JWT::urlsafeB64Encode($signature);
65 |
66 | return implode('.', $segments);
67 | }
68 |
69 | /**
70 | * @param string $msg The message to sign
71 | * @param string $key The secret key
72 | * @param string $method The signing algorithm
73 | *
74 | * @return string An encrypted message
75 | */
76 | public static function sign($msg, $key, $method = 'HS256')
77 | {
78 | $methods = array(
79 | 'HS256' => 'sha256',
80 | 'HS384' => 'sha384',
81 | 'HS512' => 'sha512',
82 | );
83 | if (empty($methods[$method])) {
84 | throw new DomainException('Algorithm not supported');
85 | }
86 | return hash_hmac($methods[$method], $msg, $key, true);
87 | }
88 |
89 | /**
90 | * @param string $input JSON string
91 | *
92 | * @return object Object representation of JSON string
93 | */
94 | public static function jsonDecode($input)
95 | {
96 | $obj = json_decode($input);
97 | if (function_exists('json_last_error') && $errno = json_last_error()) {
98 | JWT::handleJsonError($errno);
99 | }
100 | else if ($obj === null && $input !== 'null') {
101 | throw new DomainException('Null result with non-null input');
102 | }
103 | return $obj;
104 | }
105 |
106 | /**
107 | * @param object|array $input A PHP object or array
108 | *
109 | * @return string JSON representation of the PHP object or array
110 | */
111 | public static function jsonEncode($input)
112 | {
113 | $json = json_encode($input);
114 | if (function_exists('json_last_error') && $errno = json_last_error()) {
115 | JWT::handleJsonError($errno);
116 | }
117 | else if ($json === 'null' && $input !== null) {
118 | throw new DomainException('Null result with non-null input');
119 | }
120 | return $json;
121 | }
122 |
123 | /**
124 | * @param string $input A base64 encoded string
125 | *
126 | * @return string A decoded string
127 | */
128 | public static function urlsafeB64Decode($input)
129 | {
130 | $remainder = strlen($input) % 4;
131 | if ($remainder) {
132 | $padlen = 4 - $remainder;
133 | $input .= str_repeat('=', $padlen);
134 | }
135 | return base64_decode(strtr($input, '-_', '+/'));
136 | }
137 |
138 | /**
139 | * @param string $input Anything really
140 | *
141 | * @return string The base64 encode of what you passed in
142 | */
143 | public static function urlsafeB64Encode($input)
144 | {
145 | return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
146 | }
147 |
148 | /**
149 | * @param int $errno An error number from json_last_error()
150 | *
151 | * @return void
152 | */
153 | private static function handleJsonError($errno)
154 | {
155 | $messages = array(
156 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
157 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
158 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
159 | );
160 | throw new DomainException(isset($messages[$errno])
161 | ? $messages[$errno]
162 | : 'Unknown JSON error: ' . $errno
163 | );
164 | }
165 |
166 | }
167 |
168 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011, Neuman Vong
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Neuman Vong nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: test
2 | test:
3 | @echo running tests
4 | @phpunit --configuration tests/phpunit.xml
5 |
6 | .PHONY: all test
7 |
--------------------------------------------------------------------------------
/package.php:
--------------------------------------------------------------------------------
1 | setOptions(
20 | array(
21 | 'filelistgenerator' => 'file',
22 | 'simpleoutput' => true,
23 | 'baseinstalldir' => '/',
24 | 'packagedirectory' => './',
25 | 'dir_roles' => array(
26 | 'tests' => 'test'
27 | ),
28 | 'ignore' => array(
29 | 'package.php',
30 | '*.tgz'
31 | )
32 | )
33 | );
34 |
35 | $package->setPackage('JWT');
36 | $package->setSummary('A JWT encoder/decoder.');
37 | $package->setDescription($description);
38 | $package->setChannel('pear.php.net');
39 | $package->setPackageType('php');
40 | $package->setLicense(
41 | 'MIT License',
42 | 'http://creativecommons.org/licenses/MIT/'
43 | );
44 |
45 | $package->setNotes($release_notes);
46 | $package->setReleaseVersion($release_version);
47 | $package->setReleaseStability($release_state);
48 | $package->setAPIVersion($api_version);
49 | $package->setAPIStability($api_state);
50 |
51 | $package->addMaintainer(
52 | 'lead',
53 | 'lcfrs',
54 | 'Neuman Vong',
55 | 'neuman+pear@twilio.com'
56 | );
57 |
58 | $package->addExtensionDep('required', 'json');
59 | $package->addExtensionDep('required', 'hash');
60 |
61 | $package->setPhpDep('5.1');
62 |
63 | $package->setPearInstallerDep('1.7.0');
64 | $package->generateContents();
65 | $package->addRelease();
66 |
67 | if ( isset($_GET['make'])
68 | || (isset($_SERVER['argv']) && @$_SERVER['argv'][1] == 'make')
69 | ) {
70 | $package->writePackageFile();
71 | } else {
72 | $package->debugPackageFile();
73 | }
74 |
75 | ?>
76 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 | JWT
7 | pear.php.net
8 | A JWT encoder/decoder.
9 | A JWT encoder/decoder.
10 |
11 | Neuman Vong
12 | lcfrs
13 | neuman+pear@twilio.com
14 | yes
15 |
16 | 2011-03-25
17 |
18 |
19 | 0.0.0
20 | 0.0.0
21 |
22 |
23 | alpha
24 | alpha
25 |
26 | MIT License
27 |
28 | No release notes.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 5.1
45 |
46 |
47 | 1.7.0
48 |
49 |
50 | json
51 |
52 |
53 | hash
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 0.0.0
62 | 0.0.0
63 |
64 |
65 | alpha
66 | alpha
67 |
68 | 2011-03-25
69 | MIT License
70 |
71 | No release notes.
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/tests/Bootstrap.php:
--------------------------------------------------------------------------------
1 | assertEquals(JWT::decode($msg, 'my_key'), 'abc');
7 | }
8 |
9 | function testDecodeFromPython() {
10 | $msg = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.Iio6aHR0cDovL2FwcGxpY2F0aW9uL2NsaWNreT9ibGFoPTEuMjMmZi5vbz00NTYgQUMwMDAgMTIzIg.E_U8X2YpMT5K1cEiT_3-IvBYfrdIFIeVYeOqre_Z5Cg';
11 | $this->assertEquals(
12 | JWT::decode($msg, 'my_key'),
13 | '*:http://application/clicky?blah=1.23&f.oo=456 AC000 123'
14 | );
15 | }
16 |
17 | function testUrlSafeCharacters() {
18 | $encoded = JWT::encode('f?', 'a');
19 | $this->assertEquals('f?', JWT::decode($encoded, 'a'));
20 | }
21 |
22 | function testMalformedUtf8StringsFail() {
23 | $this->setExpectedException('DomainException');
24 | JWT::encode(pack('c', 128), 'a');
25 | }
26 |
27 | function testMalformedJsonThrowsException() {
28 | $this->setExpectedException('DomainException');
29 | JWT::jsonDecode('this is not valid JSON string');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ./
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------