mirror of
https://github.com/elyby/oauth2-server.git
synced 2025-05-31 14:12:07 +05:30
Compare commits
2117 Commits
0.4.2
...
export_red
Author | SHA1 | Date | |
---|---|---|---|
|
677c10a61d | ||
|
3684a76ade | ||
|
0b0b43d433 | ||
|
cd8742f630 | ||
|
2097edd6eb | ||
|
705120c974 | ||
|
8a78e00a2e | ||
|
3413c20590 | ||
|
e1dc4d708c | ||
|
18dabd36e3 | ||
|
1a3107b4fc | ||
|
1d9ca35fec | ||
|
c7f998ee02 | ||
|
4b1c9ed503 | ||
|
dc3c74601a | ||
|
f5e910e6ec | ||
|
2b7923c593 | ||
|
3f95c0d11e | ||
|
4be97e6fd0 | ||
|
aba5353257 | ||
|
7f0879b8b4 | ||
|
cb9aa25c89 | ||
|
a6a499f8fb | ||
|
ccf36588ee | ||
|
6b2a3db185 | ||
|
1a6ebdf81c | ||
|
46c86ed5b1 | ||
|
a92a274d15 | ||
|
c4c354e2df | ||
|
7bc1ec643e | ||
|
51b97f87c1 | ||
|
e3b23fa826 | ||
|
bac79a26a8 | ||
|
012808f094 | ||
|
0db54cf1e5 | ||
|
c7d047f7f5 | ||
|
e1324b88b2 | ||
|
c60e8e3581 | ||
|
e0ee244506 | ||
|
8b5841870f | ||
|
048e45d8cd | ||
|
bf75596989 | ||
|
c5cfc0a371 | ||
|
5ab4323856 | ||
|
28709f300f | ||
|
bd483d701b | ||
|
3dc324af6e | ||
|
17923634bf | ||
|
a1cf22a3a9 | ||
|
86d1581cd9 | ||
|
521ed9a8cb | ||
|
1bbcb57d63 | ||
|
93d4b947d8 | ||
|
27d5c5ed8d | ||
|
4ecd3131c1 | ||
|
3fdfbe11f6 | ||
|
42df2d9c47 | ||
|
2eb1cf79e5 | ||
|
382b6f5fbf | ||
|
86869eafbb | ||
|
9236e842d9 | ||
|
9bc7f6c8c5 | ||
|
1e9a468e66 | ||
|
c7f4998497 | ||
|
0a78236f17 | ||
|
a68f8001a4 | ||
|
b88198a9a4 | ||
|
8cf39fd9cd | ||
|
6f6820f629 | ||
|
0742d5150c | ||
|
64f0d89fad | ||
|
ebf78132d7 | ||
|
aa5bbe5f06 | ||
|
66d4ce6de8 | ||
|
2ea76ca4fd | ||
|
b2840474fd | ||
|
0227f14b7b | ||
|
fad42a88fd | ||
|
d7defafd83 | ||
|
16f37560d4 | ||
|
5ed8e59ef3 | ||
|
c2cd12e0b8 | ||
|
8e9368cf44 | ||
|
894724c45b | ||
|
fd65bf9e54 | ||
|
2a16dbeb7f | ||
|
faa350792a | ||
|
b6955a6c65 | ||
|
ec8a663a81 | ||
|
dc3181bbb0 | ||
|
1e3a7adb19 | ||
|
b71f382cd7 | ||
|
9783388523 | ||
|
46493c461e | ||
|
8b421818f2 | ||
|
b09154af33 | ||
|
f1454cde36 | ||
|
f2cd3646ff | ||
|
7839a61170 | ||
|
443d7c485a | ||
|
a61c6a318a | ||
|
eea9c30e70 | ||
|
a936962716 | ||
|
685dc6edea | ||
|
2b4974b697 | ||
|
94e75ba6f3 | ||
|
efa8ef6fce | ||
|
7982275757 | ||
|
f6c1070ccc | ||
|
d64fb3f526 | ||
|
95a9f4649d | ||
|
4bb5b747c1 | ||
|
5868996961 | ||
|
9542af627e | ||
|
3b983ad0b4 | ||
|
34ec35019b | ||
|
16f9de86f2 | ||
|
ac818bd921 | ||
|
73698e28d9 | ||
|
c87be9477c | ||
|
d288a2ad8a | ||
|
a34f5dd7db | ||
|
c0efdf0dd0 | ||
|
f96fca3b48 | ||
|
20b355b025 | ||
|
793f65d3a3 | ||
|
322b55eddf | ||
|
50ab9dd8ac | ||
|
b624124d5a | ||
|
dbf2b55bc5 | ||
|
b11d628e8b | ||
|
0515129c9c | ||
|
50566cdc87 | ||
|
b4d88995de | ||
|
398029be56 | ||
|
30ed221481 | ||
|
939c0619d0 | ||
|
4042a31159 | ||
|
0bdd02cdb4 | ||
|
7bf7700645 | ||
|
d76025d613 | ||
|
d6792c1662 | ||
|
9882f6716c | ||
|
71c605117a | ||
|
6bc6ac09d2 | ||
|
fe421878e6 | ||
|
0c2356a508 | ||
|
9645119ccb | ||
|
da2742bea7 | ||
|
efb5ce5e2a | ||
|
1ddc27e792 | ||
|
b7b7dda28c | ||
|
ef864b5cba | ||
|
fcd6eb8a3c | ||
|
133d9cc97a | ||
|
592dd2f433 | ||
|
4a464dd336 | ||
|
970df8f34b | ||
|
6a1645aebc | ||
|
e3e7abf41e | ||
|
d831868d58 | ||
|
36bf4ff8f2 | ||
|
07ebe43b91 | ||
|
5d3d9d95be | ||
|
e85a8e31e8 | ||
|
de899fbe0a | ||
|
3eabbafe5b | ||
|
cfa9b8d3b4 | ||
|
060a090479 | ||
|
46c2f99b06 | ||
|
27b956c149 | ||
|
6949a007e5 | ||
|
74495cac49 | ||
|
fb43801458 | ||
|
8ab27ede39 | ||
|
0105a20126 | ||
|
491852b521 | ||
|
7f2fd7b22c | ||
|
abef682031 | ||
|
04807a1e2a | ||
|
d07b5a4a03 | ||
|
838f206832 | ||
|
972808561d | ||
|
5ad00b0e33 | ||
|
f49cc65c13 | ||
|
acf16e924a | ||
|
a479b5762e | ||
|
dc2a048b95 | ||
|
0c542637fe | ||
|
0cdd535f7d | ||
|
2fcee76d13 | ||
|
574299d862 | ||
|
dad3b1e1c9 | ||
|
7df0dfff9d | ||
|
ca5fe10934 | ||
|
369c7005a3 | ||
|
8184f771d4 | ||
|
51b3b415b4 | ||
|
e3ad09d4a2 | ||
|
aeb1fe48d3 | ||
|
f54980da25 | ||
|
105834af96 | ||
|
ffffc4bfeb | ||
|
a77732e97c | ||
|
614bba2c11 | ||
|
224763cda6 | ||
|
a31bc7d4cc | ||
|
0d20c755d4 | ||
|
e36ff17ad9 | ||
|
a339d99135 | ||
|
a7a1065e38 | ||
|
09bf988922 | ||
|
a571e2262b | ||
|
519543e925 | ||
|
3614f8bd7c | ||
|
e4a7fea834 | ||
|
68c9fbd83c | ||
|
466e1a639d | ||
|
d7ab153500 | ||
|
72ead2e3ce | ||
|
ae4ab26aaf | ||
|
ca6be0577c | ||
|
ef75d13255 | ||
|
bd741e9203 | ||
|
aac64e49cf | ||
|
61156ef8c7 | ||
|
2a7f671a95 | ||
|
02609c37cc | ||
|
9941a96feb | ||
|
4b0383b16c | ||
|
4aeb92aa98 | ||
|
b182389395 | ||
|
92f598d1dc | ||
|
a40092ff54 | ||
|
2e47fa7fca | ||
|
fc55621f20 | ||
|
beec37d95f | ||
|
98812e6fab | ||
|
354aed7671 | ||
|
5a499bf03c | ||
|
2e3ee60a2a | ||
|
0242d0c996 | ||
|
3ea0cdc936 | ||
|
19d782d223 | ||
|
8a25e0a01b | ||
|
a3d4f583ed | ||
|
28276cb688 | ||
|
b1b33207ab | ||
|
793000f149 | ||
|
f8c2e721a0 | ||
|
cbce5f45ba | ||
|
c2dcdee266 | ||
|
33ce849617 | ||
|
ff5e9f57a5 | ||
|
577065c270 | ||
|
a1da9beb92 | ||
|
48ce5f36cf | ||
|
fd72d79ad3 | ||
|
35c6f28aef | ||
|
bd47b58f81 | ||
|
e38ea4ab34 | ||
|
8b40858509 | ||
|
aa5cc5eac7 | ||
|
38f26d5229 | ||
|
a5a51fad95 | ||
|
52d7952ba5 | ||
|
2375b8c7f3 | ||
|
f9a91a79c2 | ||
|
242dd4dcfe | ||
|
c94ec122aa | ||
|
05f5d90034 | ||
|
491c23c1e9 | ||
|
27323b5c9a | ||
|
80bc291c51 | ||
|
8a619e5c1e | ||
|
7e07033b10 | ||
|
6991777ff3 | ||
|
9febc32e14 | ||
|
c8b44ff5c7 | ||
|
9fc288ce53 | ||
|
8f1bf88792 | ||
|
cc19da50b4 | ||
|
bec0de16bb | ||
|
a56acc8dd0 | ||
|
c9b07f386c | ||
|
00a7972f74 | ||
|
e3266cb50a | ||
|
2fdd6ce494 | ||
|
6fd3024c48 | ||
|
62e06b7d3a | ||
|
009c109716 | ||
|
6723aadfe8 | ||
|
e24964af07 | ||
|
99e42f6f25 | ||
|
6700b113a8 | ||
|
28e1418f64 | ||
|
143afc9561 | ||
|
456c6cfdd2 | ||
|
4f68d2a5f2 | ||
|
49f66866f7 | ||
|
028d91f670 | ||
|
9287f587fc | ||
|
70396bec67 | ||
|
6679418436 | ||
|
e04b8f4e6d | ||
|
e0cc5ee1b0 | ||
|
b78c012796 | ||
|
bcd2fc38c0 | ||
|
25c3c216a0 | ||
|
8bbb20a012 | ||
|
cd5233392e | ||
|
7a6c35bc29 | ||
|
8614aea887 | ||
|
7a5c511807 | ||
|
e0b65a2831 | ||
|
ee7d52ecaa | ||
|
7d1d88cdf1 | ||
|
80a949601f | ||
|
fea577f25b | ||
|
1f87c7a7be | ||
|
3098f6d7fa | ||
|
06a23a1dd0 | ||
|
97089ad49e | ||
|
eca385ab08 | ||
|
ef06c29ee8 | ||
|
5fb9fc929a | ||
|
4c548dbd78 | ||
|
b3cd73cac7 | ||
|
3999c41fef | ||
|
ce2662ece7 | ||
|
d2641b560d | ||
|
8bbd218856 | ||
|
eb9cde5ab7 | ||
|
1b692e2298 | ||
|
d22f222e65 | ||
|
cf9acb32b8 | ||
|
92d8052a5b | ||
|
a3289c6ecb | ||
|
292272d128 | ||
|
ef8a741527 | ||
|
91d9c11fb4 | ||
|
2ec8d148b0 | ||
|
01d21b2533 | ||
|
ff29721ca9 | ||
|
5b79b40df9 | ||
|
b6d9835281 | ||
|
57ca83a8ba | ||
|
41bba7f58c | ||
|
dcae4af6ce | ||
|
a0cabb573c | ||
|
276d5b655b | ||
|
00138446b6 | ||
|
1c36b70dab | ||
|
f11e4c81cd | ||
|
f5a1feb67d | ||
|
1ad4d2121f | ||
|
1660dd0559 | ||
|
9ceafe5dd3 | ||
|
d1d68242ea | ||
|
a53e753b1a | ||
|
f88961eddd | ||
|
8c93fd74c9 | ||
|
2765481b9f | ||
|
9273936009 | ||
|
5f4ec6a154 | ||
|
b50c7622db | ||
|
dc9c1a1023 | ||
|
6e6baf5b75 | ||
|
7878cf9c13 | ||
|
1bcee9aaba | ||
|
1e3a84fc85 | ||
|
a5c5929dc9 | ||
|
c6bf2e1df0 | ||
|
eb645063c7 | ||
|
65789e0f39 | ||
|
512d4898e2 | ||
|
c895885700 | ||
|
661a0994c6 | ||
|
3871aee48c | ||
|
04f3d39b45 | ||
|
6bb416ce78 | ||
|
ce8248c10f | ||
|
13be557825 | ||
|
0f08063864 | ||
|
cc6eb63dd8 | ||
|
093c7755fa | ||
|
82b81c7f6f | ||
|
9cd86a9154 | ||
|
42ea0de9fb | ||
|
ab760a805c | ||
|
34cb0ba759 | ||
|
ac48653bb5 | ||
|
bd2cdaf5da | ||
|
4806eda45a | ||
|
b2fe909a71 | ||
|
d0619385b8 | ||
|
f79d3f27cf | ||
|
3828f87b19 | ||
|
a49f6ff80d | ||
|
1161ceda0d | ||
|
f9143b5163 | ||
|
90fec63104 | ||
|
4563685375 | ||
|
cc98b5ec16 | ||
|
4d77aee4a9 | ||
|
b6b0e7c944 | ||
|
a4fc05c31e | ||
|
606f69e6cd | ||
|
825017f27e | ||
|
37645f3244 | ||
|
63c2c21ee6 | ||
|
23c7138d48 | ||
|
63861704b6 | ||
|
203be5ca20 | ||
|
24f29b6382 | ||
|
8c788e9fc8 | ||
|
4d28eadf93 | ||
|
5a28fb8af4 | ||
|
c996b66528 | ||
|
c70451abd5 | ||
|
e7ee483d11 | ||
|
5b223a9501 | ||
|
4270f5bac1 | ||
|
3b58ab1df2 | ||
|
925776958f | ||
|
c86c7dde70 | ||
|
e184691ded | ||
|
b2648218f1 | ||
|
2aca909d20 | ||
|
79038ced78 | ||
|
7c2218fdcc | ||
|
935fff8308 | ||
|
0f1ddaaacf | ||
|
ecc07abb33 | ||
|
a1b8d87b47 | ||
|
80fc8e654b | ||
|
88ccb6ff13 | ||
|
e2f9b73df3 | ||
|
fbb3586cae | ||
|
317f46b7ae | ||
|
88bf8b2367 | ||
|
315d079033 | ||
|
2824f7d27e | ||
|
0a6a4deca6 | ||
|
00c645545a | ||
|
417a64ad43 | ||
|
f5c3ba0b24 | ||
|
e1ef133067 | ||
|
523434902c | ||
|
aac467e616 | ||
|
76c2b6f88c | ||
|
72349ef22f | ||
|
850793ab88 | ||
|
0f73bf0054 | ||
|
7953f27b38 | ||
|
cc2c3a7044 | ||
|
06424fdbe2 | ||
|
55f93f9400 | ||
|
aee1779432 | ||
|
09c167ac43 | ||
|
765a01021b | ||
|
0706d66c76 | ||
|
e123fe82d0 | ||
|
107cfc3678 | ||
|
1954120c3d | ||
|
dd5eee150d | ||
|
76c1349181 | ||
|
1af4012df4 | ||
|
4a717104fa | ||
|
63530443fe | ||
|
2f8de3d230 | ||
|
57d199b889 | ||
|
6bdd108145 | ||
|
170ce2fd2d | ||
|
4710743b87 | ||
|
11ad87b5f5 | ||
|
880e3b4590 | ||
|
2167edf1d9 | ||
|
2482630221 | ||
|
83228bdcd5 | ||
|
d73b15ae32 | ||
|
945624eb51 | ||
|
bf7084a147 | ||
|
13c608b849 | ||
|
d8ece093d5 | ||
|
ded7c1ed47 | ||
|
6426e597a3 | ||
|
0da70c916a | ||
|
90cb1bf012 | ||
|
b32204bd91 | ||
|
518c1fcec5 | ||
|
ee8841fe66 | ||
|
6946592553 | ||
|
25580b98b7 | ||
|
f78dc2eca0 | ||
|
105b3116dc | ||
|
01677a564e | ||
|
4c4b0633b1 | ||
|
c4a75b2880 | ||
|
e091d48127 | ||
|
a798cfdc5d | ||
|
56e8d374fb | ||
|
b1bfff7325 | ||
|
32cde01ab2 | ||
|
11ccc305d0 | ||
|
d7df2f7e24 | ||
|
b8b92e5925 | ||
|
0ebdcd2ab8 | ||
|
9dee08ba3d | ||
|
065ef5db99 | ||
|
039537ebe2 | ||
|
d8930af5ee | ||
|
ada8d20be6 | ||
|
090c01d3d1 | ||
|
4b6ba5859c | ||
|
57323f38f7 | ||
|
46cd448a47 | ||
|
c874c59b9c | ||
|
c3a4670c11 | ||
|
17b6e2a207 | ||
|
54422a244f | ||
|
9899aa1f99 | ||
|
32efd091a1 | ||
|
655a4b2715 | ||
|
68e4b1d390 | ||
|
5ee1583c5b | ||
|
66de05a395 | ||
|
df20da1235 | ||
|
7321622104 | ||
|
84187041bd | ||
|
9eccc40eb6 | ||
|
8b865cc523 | ||
|
9775c0076b | ||
|
b68ef973df | ||
|
c6e5f12a7c | ||
|
6b88cbeb13 | ||
|
64a0fcb3a6 | ||
|
78dbb267ed | ||
|
22e6a350dd | ||
|
c0936cc320 | ||
|
bb82651bec | ||
|
9a58bc15f6 | ||
|
599c9aba75 | ||
|
4c6c189dff | ||
|
8e8aed1a50 | ||
|
7285ede563 | ||
|
4a4f4fe2d7 | ||
|
6b18a9441a | ||
|
44ff7b33a1 | ||
|
db055f790d | ||
|
d1bc4848c8 | ||
|
cf63403585 | ||
|
cdf43e498e | ||
|
a12fc98b0d | ||
|
019d285235 | ||
|
0bb968f413 | ||
|
88b19ad2d0 | ||
|
6856699cab | ||
|
72cd9a62e1 | ||
|
acf262f879 | ||
|
5241309bdb | ||
|
9a93dca05c | ||
|
a6b7a5cedc | ||
|
78b6bddc4d | ||
|
14b6761c0f | ||
|
7c61922f07 | ||
|
20535ad95b | ||
|
e885114714 | ||
|
f80d0d39a4 | ||
|
7bfd5b7d0d | ||
|
143a2e32f7 | ||
|
8f418cff08 | ||
|
fcec1f3442 | ||
|
46e7eef14e | ||
|
51f44fdf17 | ||
|
f8b2e80ef3 | ||
|
7045785d89 | ||
|
301ddc53c7 | ||
|
2a6f900323 | ||
|
fb8f47e868 | ||
|
5b192b3548 | ||
|
bf55ce1f73 | ||
|
4942585f4f | ||
|
1575128162 | ||
|
78c2067698 | ||
|
f765f134c9 | ||
|
257318e524 | ||
|
77737e7894 | ||
|
f007e25070 | ||
|
25c2e9b31b | ||
|
6ed9cbf701 | ||
|
7c35778316 | ||
|
f6f39698d9 | ||
|
3904767873 | ||
|
c3a7c418da | ||
|
af5a06098b | ||
|
6205611a71 | ||
|
9f3648039b | ||
|
08c356a1e1 | ||
|
70e32ce9bf | ||
|
94a1c18fa9 | ||
|
88b01b792a | ||
|
0178a837d4 | ||
|
2025bd6a30 | ||
|
e7f18911f3 | ||
|
8e8ac35dcb | ||
|
16ed4ea51c | ||
|
de635f826f | ||
|
3e8577f889 | ||
|
525b9b3d3e | ||
|
f7413c2f15 | ||
|
6e583fdf8a | ||
|
f9bde23799 | ||
|
2328f59601 | ||
|
103b0cc50d | ||
|
eb7526ae97 | ||
|
03e8eb6157 | ||
|
7b803365f9 | ||
|
204706f1ff | ||
|
4c6dab3f55 | ||
|
54bedda11b | ||
|
883ba8b573 | ||
|
0d1e61422a | ||
|
a722659200 | ||
|
a80310b01c | ||
|
c017b59342 | ||
|
920c0c296a | ||
|
b0db04461f | ||
|
495b55d1e8 | ||
|
92a483b3bd | ||
|
6083870603 | ||
|
6359535e32 | ||
|
5969082963 | ||
|
7a6d9a4510 | ||
|
7c86d3b848 | ||
|
d3a7b442ce | ||
|
ba30e34511 | ||
|
e24dff2723 | ||
|
625876f7ae | ||
|
4f2dfc20b9 | ||
|
1512960d92 | ||
|
273ea0ba68 | ||
|
096a4a2883 | ||
|
a68f07f734 | ||
|
a0c4900ee7 | ||
|
4c0c10ae98 | ||
|
0fb0100088 | ||
|
8f50e58ba9 | ||
|
8225b4e697 | ||
|
236a3a0358 | ||
|
b00a4e169e | ||
|
c034c3b13c | ||
|
287c371586 | ||
|
634578997f | ||
|
b8c5056c31 | ||
|
79aa1988d8 | ||
|
7c35985c1e | ||
|
c75d0e0f0e | ||
|
5d3516c7b4 | ||
|
d4fb00628e | ||
|
4bc835c007 | ||
|
fdb1d70874 | ||
|
6f71a2d178 | ||
|
651709b70f | ||
|
3f6e91575d | ||
|
8f5e0ce9f7 | ||
|
5410a42bb6 | ||
|
b7064befe4 | ||
|
44937f3600 | ||
|
76ea6b5a6c | ||
|
4689802c30 | ||
|
6ee71754c4 | ||
|
b3329dbeac | ||
|
0ca2511d1e | ||
|
2c2ef800d4 | ||
|
d8d49f742e | ||
|
e758121458 | ||
|
47656cd9b5 | ||
|
b59106dc64 | ||
|
c6faa228fe | ||
|
4eee48ca4e | ||
|
00518dded7 | ||
|
3615cbeedf | ||
|
6773db66c6 | ||
|
5ca2152313 | ||
|
5cba35456f | ||
|
be9bd76f35 | ||
|
3c0a7f14ab | ||
|
198f4c4b6f | ||
|
6f0a0cca4e | ||
|
5430ddb230 | ||
|
1ccfd9be32 | ||
|
a83c56f570 | ||
|
d7dd07cf18 | ||
|
0fed56a265 | ||
|
fc9e912e06 | ||
|
39281a6f38 | ||
|
656a8d7a56 | ||
|
6c942f25f4 | ||
|
8274c56fc2 | ||
|
de8f6ff539 | ||
|
8f69f4f9a9 | ||
|
4d2ccac8ed | ||
|
a38b7f97f9 | ||
|
197657f2b9 | ||
|
e513b42117 | ||
|
b1ce1f872b | ||
|
9533595394 | ||
|
5b977b1495 | ||
|
1de13cf892 | ||
|
1c47ec51f8 | ||
|
fbf4388b01 | ||
|
a867fdd891 | ||
|
1041a39d08 | ||
|
e23570535f | ||
|
b1cf6a8436 | ||
|
d8e1e0e00e | ||
|
fe0ed765a5 | ||
|
7ce31bda87 | ||
|
630a92b45f | ||
|
2b76e2bf6e | ||
|
115237bc1a | ||
|
6383a58755 | ||
|
614fbde56e | ||
|
0fbe109e20 | ||
|
57216984e9 | ||
|
267bd3c5d4 | ||
|
a18b8c57b2 | ||
|
cca401e66e | ||
|
55ff59edf4 | ||
|
a49c762683 | ||
|
b5b5d9f347 | ||
|
07b2758cba | ||
|
864a27f2c8 | ||
|
a698a4da7e | ||
|
1ad44d1ce0 | ||
|
b480373249 | ||
|
95cdaae17f | ||
|
61986db5ee | ||
|
878afeb9f9 | ||
|
e9dd1478c4 | ||
|
945731cb39 | ||
|
f688401f63 | ||
|
09770dc537 | ||
|
dcf3f50b87 | ||
|
59e8785f50 | ||
|
ca54a387c8 | ||
|
6fea054381 | ||
|
ed17b6540e | ||
|
e27b13ee7d | ||
|
67f835a606 | ||
|
8685006743 | ||
|
400eae153b | ||
|
c880d5c1ec | ||
|
9ee2e7271f | ||
|
b629b5e53f | ||
|
4b775fe241 | ||
|
8196f5c832 | ||
|
890fdeba16 | ||
|
c2e83ff359 | ||
|
51a1a75d37 | ||
|
251190d828 | ||
|
a350705a01 | ||
|
7293ff9e9d | ||
|
c207addf87 | ||
|
3af75729b8 | ||
|
7b8d9c9af3 | ||
|
15b6506644 | ||
|
bd12c8b1a9 | ||
|
c3c49c83f9 | ||
|
2f459b6470 | ||
|
ee91072455 | ||
|
25f93071ea | ||
|
079ed0040a | ||
|
5bbf703711 | ||
|
d635b3484b | ||
|
3365f3d733 | ||
|
9e828f8f3c | ||
|
c7a5a57304 | ||
|
ae0edc40aa | ||
|
264bc7b625 | ||
|
7159352108 | ||
|
a70bc2360a | ||
|
472ec68bbe | ||
|
e946c1e106 | ||
|
4c392db673 | ||
|
66e473b1f0 | ||
|
91c8daeb99 | ||
|
9ffe806112 | ||
|
9e04da01de | ||
|
592f60de70 | ||
|
5ae9827d67 | ||
|
5e8d2fe0f5 | ||
|
9b665f494f | ||
|
ced63e2051 | ||
|
c3ffed2daf | ||
|
ecad2b98ae | ||
|
a0402f1994 | ||
|
6e74de50e5 | ||
|
4ab9c52767 | ||
|
2b2d4a3df7 | ||
|
0ae8863322 | ||
|
4c55b6879d | ||
|
9590550799 | ||
|
c40a10a071 | ||
|
f7f3cdee24 | ||
|
edf0ee8622 | ||
|
c490cd4ef2 | ||
|
a716a08be6 | ||
|
3b4a8cf5f3 | ||
|
8f0cb0e78c | ||
|
fb1fa71b5d | ||
|
0216638903 | ||
|
5074ad9a6c | ||
|
721a31534e | ||
|
01517bb57a | ||
|
0f2c6e7f4e | ||
|
320d9e65d5 | ||
|
1218cede79 | ||
|
8ff0cb6495 | ||
|
1bdeb71efb | ||
|
1632b80631 | ||
|
5760450854 | ||
|
9c66688d19 | ||
|
da53067e63 | ||
|
84172c0d29 | ||
|
997d390f3d | ||
|
e2794c47af | ||
|
0d0aaa8764 | ||
|
ad270f7d9d | ||
|
a1bdaae9a9 | ||
|
e08669d50c | ||
|
d02437dd73 | ||
|
cee4147688 | ||
|
eedcfe115c | ||
|
f06adb38cd | ||
|
9675dff220 | ||
|
2488cbd55d | ||
|
27d4441d1d | ||
|
bc82f5badd | ||
|
7f539f8736 | ||
|
a4b65241ad | ||
|
8b601d79b9 | ||
|
d0878300d0 | ||
|
97c138bb0b | ||
|
937d425e4c | ||
|
ff5ea52ccd | ||
|
a2460886f6 | ||
|
65bcc97fc3 | ||
|
60c45ab8fe | ||
|
a644eacea7 | ||
|
13baa0bb26 | ||
|
e8a01c3bcd | ||
|
064eb85f4e | ||
|
ad5b242d10 | ||
|
704e114568 | ||
|
73cd377c4b | ||
|
3b36ae9000 | ||
|
7f67000d53 | ||
|
de000b72a4 | ||
|
e808528cc8 | ||
|
fb77a78fb3 | ||
|
0d8cb0d06f | ||
|
fc53d636f5 | ||
|
dbcaaa1f35 | ||
|
5d6634aa9f | ||
|
099c9ce41b | ||
|
186853390a | ||
|
335630f150 | ||
|
de13e14cdd | ||
|
2bd45f2a6b | ||
|
e20c529f39 | ||
|
08ad67e401 | ||
|
7f2fd69d0a | ||
|
29068dd84c | ||
|
174ae490fc | ||
|
fa3fb36ed8 | ||
|
21e2ccd0fb | ||
|
aa0570c932 | ||
|
9a8b7ec898 | ||
|
1f6bb40952 | ||
|
2f914a0aa3 | ||
|
95e3c1d1a2 | ||
|
64d4c4a38a | ||
|
655f6b9771 | ||
|
d95958bae4 | ||
|
85b9412813 | ||
|
1a5030200a | ||
|
796106b6c1 | ||
|
4234b69f3a | ||
|
6dd4caf056 | ||
|
f6cc8bbb42 | ||
|
0115c41eea | ||
|
f314154216 | ||
|
9b97778618 | ||
|
5e326d9e45 | ||
|
d2760e4ec7 | ||
|
2025749fa4 | ||
|
1c913fe75e | ||
|
556c9fa782 | ||
|
c5bc63027f | ||
|
ac9955b393 | ||
|
fccb06ed67 | ||
|
f29703ea24 | ||
|
dcc3f5d856 | ||
|
264eba9f20 | ||
|
c2c199cf98 | ||
|
0b6bcad9fb | ||
|
38a7e53cb5 | ||
|
f4b83baf74 | ||
|
5a08a0cbe2 | ||
|
7a628409db | ||
|
c6d806d3f7 | ||
|
bfcf7af4d8 | ||
|
d96f57d27f | ||
|
95919a688e | ||
|
8b185e0580 | ||
|
ca776e83a2 | ||
|
ddf3f1b890 | ||
|
a40ac5d77b | ||
|
4bc89f3fc2 | ||
|
11d25eb5a1 | ||
|
770bda8f10 | ||
|
7a8c92b3d9 | ||
|
96620c8b3b | ||
|
92a101f263 | ||
|
b85f81c429 | ||
|
8fb64041df | ||
|
3ad97b4ef4 | ||
|
44155a8efc | ||
|
b7b1f56d0c | ||
|
3e5889e93b | ||
|
ef5904ab1a | ||
|
94cc7c2bc7 | ||
|
0490736861 | ||
|
1e1043c04f | ||
|
8591fc7686 | ||
|
86b75edca0 | ||
|
13ddec3283 | ||
|
322caa77af | ||
|
95634fb390 | ||
|
6beb8d42ff | ||
|
6cffbfe33b | ||
|
5fcb47d66a | ||
|
abaf399f5f | ||
|
cbd45cc5ab | ||
|
8566a128c8 | ||
|
419cb6d149 | ||
|
f1d06e7c33 | ||
|
212938d1e2 | ||
|
cd19f11799 | ||
|
55c8df8312 | ||
|
4862ca7d60 | ||
|
660378c7b3 | ||
|
06ee612bb1 | ||
|
3c4347e385 | ||
|
168e7640c6 | ||
|
3d08051cbb | ||
|
0486d93fa3 | ||
|
5a8659471c | ||
|
f6664c6917 | ||
|
5f22ead287 | ||
|
19b12cda8e | ||
|
6c787c374c | ||
|
cd68103267 | ||
|
6332ecfa0b | ||
|
e43d95415b | ||
|
d755a8c01d | ||
|
c7a904ca40 | ||
|
8ee4dc7eb9 | ||
|
645f719ee9 | ||
|
0cc13630cc | ||
|
e21a13c82c | ||
|
a4ce1e510e | ||
|
ad05a5cae6 | ||
|
e6cc6c35ec | ||
|
f74bca33ab | ||
|
90d9d7bdd6 | ||
|
8d8dbaea0c | ||
|
03391e9630 | ||
|
7242a8db31 | ||
|
f44b618531 | ||
|
9e4fd82763 | ||
|
0744d8e926 | ||
|
3efe7b3c0a | ||
|
44ff8692dc | ||
|
6108c06e34 | ||
|
dce1620f60 | ||
|
bcd84320da | ||
|
a40374e6ec | ||
|
748ae15376 | ||
|
7811721d28 | ||
|
c5e5ae5555 | ||
|
8f724bb720 | ||
|
e9f8e7ac19 | ||
|
c4830608a2 | ||
|
65d981ad32 | ||
|
3de1b5917a | ||
|
0fbe447862 | ||
|
84a9802a67 | ||
|
f7b3c018c5 | ||
|
a88c30cb53 | ||
|
5e6f0fc6a3 | ||
|
b57b497cb7 | ||
|
0b061e3086 | ||
|
304ea2baf4 | ||
|
56060b2c16 | ||
|
633746b02e | ||
|
94b221c8a1 | ||
|
20ad5d251c | ||
|
e95a228128 | ||
|
dc2919710c | ||
|
936b8f93ec | ||
|
c1d15aa15c | ||
|
70e9d7b699 | ||
|
79791e5848 | ||
|
0efa7cd7ea | ||
|
eef5cf39d4 | ||
|
6fb3fb5110 | ||
|
a2bbb17483 | ||
|
3135f1796e | ||
|
d565665ccb | ||
|
13a1ea6db8 | ||
|
6358be90c2 | ||
|
de89a6bc89 | ||
|
e03ad0d52f | ||
|
2a20de991b | ||
|
b8732a2f83 | ||
|
1bdad3ad14 | ||
|
fd47712060 | ||
|
6339524c86 | ||
|
5f9feda80c | ||
|
9958e1bf80 | ||
|
758471ec16 | ||
|
3fcba9339d | ||
|
f9c0cb08e0 | ||
|
a9313e76d4 | ||
|
59080a8319 | ||
|
524f04c78c | ||
|
8e04868320 | ||
|
c5db707e69 | ||
|
fa3dc4e055 | ||
|
f9b2441c41 | ||
|
09f1d0fbb1 | ||
|
ed7f78179a | ||
|
6e92239dd7 | ||
|
5ab91d7345 | ||
|
6c054dbf35 | ||
|
530cdb02f0 | ||
|
0a2d4c1649 | ||
|
bb17abfe26 | ||
|
e7e4892408 | ||
|
46648f3e80 | ||
|
6f2e2a0071 | ||
|
b7ba593856 | ||
|
c0bdd22154 | ||
|
f5d731def9 | ||
|
32b451aa21 | ||
|
96a0c34d41 | ||
|
b95780022a | ||
|
c0823c464e | ||
|
cc43a31ca6 | ||
|
da8efa20cd | ||
|
03e4ac7ea6 | ||
|
1442842da9 | ||
|
0a602cb022 | ||
|
b479cb7912 | ||
|
41c7a6e731 | ||
|
82413513e8 | ||
|
03815cec6d | ||
|
c71dc47459 | ||
|
3bcd8fc3f8 | ||
|
db6d4e0dc6 | ||
|
f19189a999 | ||
|
ec9c91cc11 | ||
|
c3457107ee | ||
|
a9f61fd3ed | ||
|
b78d8ca1d8 | ||
|
8f82e8ef86 | ||
|
d88e01c7dd | ||
|
d21374fb0b | ||
|
31e5f4d33c | ||
|
a773405adf | ||
|
ccc845b195 | ||
|
21cd917892 | ||
|
a2c418ee07 | ||
|
b220368583 | ||
|
2d26c38d6c | ||
|
eeaa68400f | ||
|
56c73d2427 | ||
|
f632fcc997 | ||
|
618d84ddcf | ||
|
ace42e89e0 | ||
|
c496df98e4 | ||
|
2496653968 | ||
|
abf66ef9c8 | ||
|
4b9ec488f4 | ||
|
726d879607 | ||
|
b256195421 | ||
|
c84ea1ea62 | ||
|
16685ccde4 | ||
|
7934c7bb53 | ||
|
c174b6fc65 | ||
|
75ced70248 | ||
|
5b7fdaeece | ||
|
430a752315 | ||
|
18b104d0ac | ||
|
2e3c6b4f3a | ||
|
8e9b12fefd | ||
|
95a2308ff6 | ||
|
9985f3eee2 | ||
|
39df4ff9b1 | ||
|
90d18c553d | ||
|
385b03db6f | ||
|
a15995c126 | ||
|
f4cfd37745 | ||
|
a0d5d5817b | ||
|
d468cbf600 | ||
|
be14b3a2df | ||
|
1f1f0d8f15 | ||
|
8fcf93c489 | ||
|
bdd71743cd | ||
|
77b5282b46 | ||
|
e88d802918 | ||
|
61ab070692 | ||
|
d3ed454881 | ||
|
b5bbf8332f | ||
|
110d5ce76f | ||
|
bf1c46d62f | ||
|
5840ace38f | ||
|
eabcf82268 | ||
|
6a78d53d03 | ||
|
b831d19f8d | ||
|
721e52c5d9 | ||
|
6e73099d8c | ||
|
784af67367 | ||
|
4f053bb63a | ||
|
5211d1902c | ||
|
ffc8823e4f | ||
|
f912f60a59 | ||
|
476b8d81c1 | ||
|
2b2067e162 | ||
|
9048617e35 | ||
|
775d42115a | ||
|
f3705865a3 | ||
|
15cef6ba16 | ||
|
72b741d7c9 | ||
|
26c1abdd3c | ||
|
56f6df11a8 | ||
|
997d4f2eb7 | ||
|
d63efc8dbf | ||
|
7a3670523d | ||
|
1e39f1d84a | ||
|
0b66fd1948 | ||
|
164de644e9 | ||
|
f1da0d2943 | ||
|
f964fd2962 | ||
|
36a1a430b5 | ||
|
3721ecb40a | ||
|
5fcf01f4c8 | ||
|
a16a1dbb7d | ||
|
f357602090 | ||
|
a48630c837 | ||
|
171be1c422 | ||
|
324b6db5e6 | ||
|
a73322fb43 | ||
|
027971776b | ||
|
7c57310b67 | ||
|
810544ec0a | ||
|
34a6b66b8c | ||
|
61738a7fe2 | ||
|
51184259d1 | ||
|
b21de11429 | ||
|
cf6e86c9d4 | ||
|
f6fdbc7142 | ||
|
7f7f45662a | ||
|
5d7eeb0512 | ||
|
742b51c2cd | ||
|
f92a68cc72 | ||
|
295d8ffa24 | ||
|
3d08140651 | ||
|
cc7596f3b3 | ||
|
ec8a8393ee | ||
|
3869b8f406 | ||
|
7da7484008 | ||
|
b42ba4af17 | ||
|
dd795a82f4 | ||
|
166362d3cd | ||
|
d43391564c | ||
|
ea6edf572a | ||
|
19b64c2e65 | ||
|
612775466c | ||
|
740ea24e08 | ||
|
e1c14abf6c | ||
|
d1aae27359 | ||
|
80aeaf9200 | ||
|
282bb20cc8 | ||
|
b727be55a2 | ||
|
cf80a2d6ce | ||
|
72a5c1794a | ||
|
707c85b0d6 | ||
|
c56562b0b8 | ||
|
d0b2498b43 | ||
|
17be6f4549 | ||
|
b50fbff1e3 | ||
|
7375a348c6 | ||
|
ae5dd9ce65 | ||
|
f9e56ff62a | ||
|
1bcf7ee20f | ||
|
bee9c6a51d | ||
|
851c7c0eb1 | ||
|
7fff4a8fe8 | ||
|
44ac01ee0e | ||
|
60bd334b46 | ||
|
7398bee59e | ||
|
40420f27ed | ||
|
d32bfaa757 | ||
|
2653a174bb | ||
|
676fb4c06a | ||
|
7f815275d6 | ||
|
a056e2fe03 | ||
|
48d9fde133 | ||
|
a12786cbd5 | ||
|
164cc6ddb9 | ||
|
27f51d33e1 | ||
|
2108c88dfb | ||
|
a1726903b5 | ||
|
8075190e0c | ||
|
3b176fe220 | ||
|
986dc59627 | ||
|
0878897969 | ||
|
0ce7ecb45a | ||
|
7a63f42462 | ||
|
774341c346 | ||
|
c8983b35a0 | ||
|
edaccab04b | ||
|
f8b61b47b9 | ||
|
b8331d12e4 | ||
|
92404ab2bf | ||
|
3b17872f10 | ||
|
8cfa3dcdad | ||
|
9ec1380889 | ||
|
2af7195f06 | ||
|
8c6fd6c05a | ||
|
2df6446eb2 | ||
|
e1c0ff2685 | ||
|
6157bd77ca | ||
|
76de634f2b | ||
|
cfada388db | ||
|
2f971dc77f | ||
|
ae7b7e9aa9 | ||
|
bed6c3287e | ||
|
f83e5a8731 | ||
|
35369038db | ||
|
6a1f927a6c | ||
|
b2c0933ee6 | ||
|
3104d13eba | ||
|
8b1f3ef193 | ||
|
1ff885cff1 | ||
|
d950797bd9 | ||
|
d6e6b8b710 | ||
|
001c15bfad | ||
|
7fbc563524 | ||
|
0d949d53f3 | ||
|
d071cd112a | ||
|
4c1cd04a24 | ||
|
30162c8899 | ||
|
b21aac0ab2 | ||
|
73917a0327 | ||
|
ad86f71b34 | ||
|
d58877131d | ||
|
846b4d1652 | ||
|
583c21e7db | ||
|
7dc5a8090f | ||
|
6b29b7450e | ||
|
b9debaab26 | ||
|
856051bfb3 | ||
|
fa55a791e7 | ||
|
5c5d7d5340 | ||
|
a2a768b6e6 | ||
|
4bbbc72035 | ||
|
3815355489 | ||
|
9bb7af6f83 | ||
|
d16b1b72ba | ||
|
e37289231d | ||
|
1c2ec943e9 | ||
|
17dfc897b4 | ||
|
7586e62da1 | ||
|
a1c3746a5a | ||
|
d23dc4d247 | ||
|
293bc52972 | ||
|
11ab167376 | ||
|
f290de6dfc | ||
|
d260167155 | ||
|
3f114dc5e3 | ||
|
fedd10b5ed | ||
|
746cd4ab7d | ||
|
5848c0d920 | ||
|
db7c42cc91 | ||
|
f01cf7ef2f | ||
|
61f8195edd | ||
|
c38d20b163 | ||
|
fbf1535db1 | ||
|
90ce1932cc | ||
|
b60693c5d6 | ||
|
736a7b95eb | ||
|
b50f7ce04e | ||
|
b2514e35f4 | ||
|
bc314f7c52 | ||
|
72e3ddad1e | ||
|
6333a975f8 | ||
|
44ab7b6135 | ||
|
bfcccb2671 | ||
|
3183828c1c | ||
|
6be7c119db | ||
|
0f13ff188a | ||
|
099f009b39 | ||
|
136edf16c5 | ||
|
62d658524b | ||
|
5b9f9a500d | ||
|
05d33f7020 | ||
|
d9bf0e5899 | ||
|
63be2684d3 | ||
|
536ef3244d | ||
|
a3f5d20592 | ||
|
1e3a192920 | ||
|
b68a5c2abb | ||
|
643c3493c4 | ||
|
64ca2a4b49 | ||
|
1ff3d1adda | ||
|
9e2a6ed238 | ||
|
be51cdf9b1 | ||
|
13cd0cacdf | ||
|
67587e450b | ||
|
6ce190d33b | ||
|
f923e89c3d | ||
|
31c3cbe593 | ||
|
f03e4a9e37 | ||
|
7d8989a8cd | ||
|
b9e12a7fec | ||
|
d32cea1988 | ||
|
ce1a650ae1 | ||
|
01e823427f | ||
|
da92410ecb | ||
|
d65bd112a9 | ||
|
2ca3df60be | ||
|
23303905a8 | ||
|
0b8e69f0d0 | ||
|
a448f2167b | ||
|
3494b65be0 | ||
|
9ff841aa6f | ||
|
dc4136a6f5 | ||
|
7e4317cf54 | ||
|
522c7478c7 | ||
|
130d42c85e | ||
|
0433791bc6 | ||
|
79f15f3855 | ||
|
0754b9ec75 | ||
|
6568ca5790 | ||
|
30abfeefbf | ||
|
1483ce936b | ||
|
661086d3b8 | ||
|
ca1b977786 | ||
|
7525fc0884 | ||
|
06d5b343d6 | ||
|
07a42f6f43 | ||
|
8be92d413d | ||
|
71ac21b70e | ||
|
12ab753f15 | ||
|
684a8a269e | ||
|
cd60c2961f | ||
|
f046a024e4 | ||
|
90e585ba9a | ||
|
7e0e337134 | ||
|
324da27ea9 | ||
|
0a260f0c8c | ||
|
1a4cc3b750 | ||
|
b3da61822e | ||
|
69208fe0ac | ||
|
806838b8e4 | ||
|
2637af87ec | ||
|
ac3e787278 | ||
|
ffe59f5a5f | ||
|
7b9899c46b | ||
|
c29340ae27 | ||
|
ad12a088cf | ||
|
0810be9ce4 | ||
|
7f75246619 | ||
|
5ec1bf8a88 | ||
|
443d72ee24 | ||
|
861da5fee9 | ||
|
f3fc921212 | ||
|
54e6bbd4a6 | ||
|
0d6c4f65b9 | ||
|
cfd1c93a46 | ||
|
29b09227ac | ||
|
6cb5863b81 | ||
|
2f14f6b391 | ||
|
94369abd60 | ||
|
45edac4216 | ||
|
5bdfc9908a | ||
|
1890d71838 | ||
|
00d5fb5834 | ||
|
49b776c495 | ||
|
31e03c2d36 | ||
|
395ee3bf49 | ||
|
6f85bcbbf1 | ||
|
47a5c1ba08 | ||
|
d123ba095c | ||
|
bfad6c7e28 | ||
|
7067a35d3a | ||
|
f40ada9ac7 | ||
|
ca61d5d4e0 | ||
|
7771bc04ec | ||
|
20032f33a2 | ||
|
b694cca743 | ||
|
1e78f62823 | ||
|
c6bc1b0cfc | ||
|
48dea185d8 | ||
|
f34dd4a0cb | ||
|
4362f17fd6 | ||
|
6d81c1e57e | ||
|
1a88d3f4c5 | ||
|
0a3215be8e | ||
|
954f29f879 | ||
|
4480aa3456 | ||
|
44db2b295f | ||
|
33f4f5b7ab | ||
|
e61782975a | ||
|
d7c1c50269 | ||
|
f2b5967f10 | ||
|
92779ad078 | ||
|
83c7dea1cc | ||
|
4486b7120f | ||
|
687e794ce3 | ||
|
c7dfc42d57 | ||
|
33c68a2103 | ||
|
92639fbbd6 | ||
|
9af1d2a201 | ||
|
f24d1be3e9 | ||
|
80802e5df4 | ||
|
6aa52adb3e | ||
|
cd767c07fa | ||
|
0b1edadaa7 | ||
|
c1269a97d6 | ||
|
5e4cd98706 | ||
|
4ebf3f838f | ||
|
11c4c93398 | ||
|
cf32b5dd1b | ||
|
8e164f4b99 | ||
|
400d4d8f1e | ||
|
5ffbe6ac37 | ||
|
f7231b2c6a | ||
|
9a6ab4141f | ||
|
ad5cef3b7d | ||
|
cd44bf8f48 | ||
|
d065549e95 | ||
|
e43bdc837c | ||
|
45b971d286 | ||
|
4096c8cd20 | ||
|
66febb7744 | ||
|
d517b4c9e1 | ||
|
2412f1f826 | ||
|
c5aee31405 | ||
|
81e9e7364b | ||
|
11664e6d37 | ||
|
d40ee11ef5 | ||
|
b9cedc8b93 | ||
|
58adefa7d0 | ||
|
61f039366b | ||
|
6a0596f40b | ||
|
e32f153acf | ||
|
4823bfde8b | ||
|
7e0115f0ad | ||
|
49650d1ae9 | ||
|
aae99c2487 | ||
|
0d293e7c30 | ||
|
be4799edc9 | ||
|
7516606fd3 | ||
|
e4c43faa33 | ||
|
87fbcb19af | ||
|
6300cd5d72 | ||
|
0b047fd8e4 | ||
|
07c04d15d7 | ||
|
54c2c48704 | ||
|
95d068e818 | ||
|
c5ffd05eee | ||
|
b3c3676381 | ||
|
7356c5ad74 | ||
|
b8e2c5a3f8 | ||
|
f7e68d6e10 | ||
|
719b87a40c | ||
|
62f5766908 | ||
|
f46c1d2aa4 | ||
|
1f61f45f5f | ||
|
8e0b525ba2 | ||
|
19bd476395 | ||
|
7fada0964d | ||
|
b82551c97d | ||
|
5c8ed58c67 | ||
|
ed7f5370ca | ||
|
97e7a00bca | ||
|
193018aecf | ||
|
76289c68da | ||
|
9f6576c0fa | ||
|
107991b0a7 | ||
|
ffc25fb276 | ||
|
97fd115530 | ||
|
8fbbc7bd07 | ||
|
228144a701 | ||
|
184fac507b | ||
|
82c10c32fd | ||
|
782f43c73a | ||
|
6e5327a0e2 | ||
|
5206d77167 | ||
|
bdd2bc322c | ||
|
e5315dc016 | ||
|
8b4b884a03 | ||
|
f78caa24bb | ||
|
79b1e39798 | ||
|
797ed66eda | ||
|
16bdc36ccb | ||
|
b5f02d0739 | ||
|
1183fe80c6 | ||
|
9f1f0cc3bc | ||
|
6981ced972 | ||
|
019dfa8836 | ||
|
7f6ca35628 | ||
|
e1a7f576e4 | ||
|
6250daabd3 | ||
|
04277aeaa0 | ||
|
647de842ff | ||
|
ed10cbb4dc | ||
|
ba5f2840fb | ||
|
e8aeaf0777 | ||
|
fcc1388aeb | ||
|
d7ddfe6452 | ||
|
785d3bd21f | ||
|
5893ba4e8e | ||
|
b2c07aa68f | ||
|
ac29fc4a62 | ||
|
f78bb954d0 | ||
|
29b0389a75 | ||
|
2aa318cfd7 | ||
|
82f7c7abaf | ||
|
2d90540531 | ||
|
de681b1ebf | ||
|
b5217271b0 | ||
|
cc1e78e1ff | ||
|
b12a1d84df | ||
|
901aab9deb | ||
|
9ac56ad547 | ||
|
c60b29d201 | ||
|
2a524efff5 | ||
|
c8c69829f0 | ||
|
22794d49d1 | ||
|
4e37d9bb61 | ||
|
af06f9f3ea | ||
|
aef86227da | ||
|
54ffa58e7b | ||
|
5f7c14789b | ||
|
0bc16c04d9 | ||
|
8a2922697f | ||
|
1045c70bac | ||
|
3c9fd6be27 | ||
|
f83a9a7fa4 | ||
|
d10cc5040d | ||
|
d314c1efd9 | ||
|
468acbc369 | ||
|
013b1b53b4 | ||
|
5254c9d225 | ||
|
e07e0dba78 | ||
|
8556f616d3 | ||
|
064d4d967c | ||
|
a85feb1a32 | ||
|
8e7a975f1a | ||
|
5829781b38 | ||
|
895a379ed3 | ||
|
e4622b1f65 | ||
|
c5f48782e6 | ||
|
9de979a4ee | ||
|
20df1f50a6 | ||
|
40ea409aed | ||
|
a5b4198cb7 | ||
|
e71eb8074c | ||
|
0c4a45f329 | ||
|
115ca30f5a | ||
|
310c00a096 | ||
|
add1aa5949 | ||
|
36760a07cc | ||
|
11e0b004bd | ||
|
a2db7e1929 | ||
|
3cd5f50e64 | ||
|
603efeb80d | ||
|
69571bc8ef | ||
|
ca3b7d51df | ||
|
ac2beb08d6 | ||
|
0250d8d4d1 | ||
|
2d90a09f65 | ||
|
e9d867ba95 | ||
|
2c732a6647 | ||
|
0b1221ac14 | ||
|
ca4763483d | ||
|
9d6ecfae46 | ||
|
a3863fec2e | ||
|
5cd420bd5d | ||
|
e62bc4e98d | ||
|
7a38187076 | ||
|
40490db27f | ||
|
bc74aff46d | ||
|
337cb088e9 | ||
|
25332be3d1 | ||
|
13b15dfa3a | ||
|
7c1b913e49 | ||
|
9e5bd4cd67 | ||
|
427ae50704 | ||
|
449ba5005c | ||
|
b86d1f1406 | ||
|
f563a59ce8 | ||
|
324a3f0cdc | ||
|
f07d169336 | ||
|
227707c5dc | ||
|
6d7887dc36 | ||
|
5870368e33 | ||
|
9b9cf79f39 | ||
|
619d755008 | ||
|
2adefdf4c8 | ||
|
b5854215a7 | ||
|
302026d437 | ||
|
8d17049877 | ||
|
1f523c3a08 | ||
|
924066166c | ||
|
0dcda992c7 | ||
|
70aafb7521 | ||
|
f59213499c | ||
|
d9917a57e1 | ||
|
dba976d6ac | ||
|
8fe3ed7eb5 | ||
|
9099173db2 | ||
|
c6ac1de26b | ||
|
e55ca5bc05 | ||
|
4febb90210 | ||
|
75482c9e20 | ||
|
0db8850e81 | ||
|
9a224bd847 | ||
|
0c36045913 | ||
|
262ce23fb9 | ||
|
1419ba8cdc | ||
|
b55b73c1e8 | ||
|
e95ae977cc | ||
|
572e6935a6 | ||
|
dd88426323 | ||
|
14683bc65e | ||
|
1d3bc85423 | ||
|
10a4bf41ed | ||
|
041104e2b1 | ||
|
031cf3064a | ||
|
954ff19823 | ||
|
5bb1359ad7 | ||
|
2bd61f040b | ||
|
bacc9ce316 | ||
|
5ec2c24b5c | ||
|
ec9a08af63 | ||
|
6cade987a2 | ||
|
44408b873f | ||
|
b4bfa69c88 | ||
|
9c3c70a5fb | ||
|
556e9cc9ab | ||
|
d3158a830b | ||
|
4a71c376b8 | ||
|
ed427cba37 | ||
|
a358835522 | ||
|
23627c659e | ||
|
bee71c1e83 | ||
|
ad97273455 | ||
|
0b55dc4c01 | ||
|
4985770d07 | ||
|
a2bf4e0dfb | ||
|
cfbb037e07 | ||
|
6c28fea213 | ||
|
e5dc3001c4 | ||
|
69531c3eb5 | ||
|
89f3446b98 | ||
|
69710a5909 | ||
|
85353c5844 | ||
|
879d98abfb | ||
|
f612e105bd | ||
|
56f63bb4c0 | ||
|
8a42bc796f | ||
|
8fa7b303fa | ||
|
55b86e26ad | ||
|
e1f09db6af | ||
|
1e28faabb9 | ||
|
9a3a91760a | ||
|
78d65e102a | ||
|
701010b129 | ||
|
84afff9ad2 | ||
|
cfc61147e1 | ||
|
66ddba808f | ||
|
6324a97118 | ||
|
4170f4e841 | ||
|
5c21370691 | ||
|
c176c1cddc | ||
|
71b6f2c2bc | ||
|
f1567df802 | ||
|
307964d571 | ||
|
a1ca904255 | ||
|
6e045afa26 | ||
|
8f15158d1c | ||
|
ca4e749986 | ||
|
cc0ab4905a | ||
|
e5a48c929b | ||
|
829cef936a | ||
|
f78e05cb08 | ||
|
0999bf4de3 | ||
|
e442253e26 | ||
|
4c4155fdac | ||
|
d901e90602 | ||
|
32a7ed38a9 | ||
|
9dec6c4bfe | ||
|
bcfb9ec475 | ||
|
98be9ab252 | ||
|
e0f4ccb775 | ||
|
dca0898c2f | ||
|
7da9e1a9d7 | ||
|
a4a8f6e661 | ||
|
a9ecca92fc | ||
|
47c24e3181 | ||
|
4d36ebd3e7 | ||
|
0b3a9dc888 | ||
|
1fcdbf45b2 | ||
|
94a064e2f4 | ||
|
92b6ce3335 | ||
|
ee9549287e | ||
|
4b3e0bf668 | ||
|
05d4b68586 | ||
|
ec9c39c108 | ||
|
5d0b295a82 | ||
|
9ec5442f90 | ||
|
4112913813 | ||
|
5eb4227709 | ||
|
f5251a6080 | ||
|
86fb02d218 | ||
|
ef4a138237 | ||
|
d531a37412 | ||
|
ca599437f6 | ||
|
252afddbd3 | ||
|
c57c4b1b4f | ||
|
ba2dc90f3b | ||
|
7373f312da | ||
|
a01810d8fa | ||
|
3ea3eb5ebd | ||
|
11022e16ef | ||
|
8d06a7b685 | ||
|
c66c8092f9 | ||
|
591139f44d | ||
|
410ad09b5c | ||
|
51138f8738 | ||
|
aa8d38108f | ||
|
9372cc85d0 | ||
|
accb80289f | ||
|
e591fbb25c | ||
|
fdb89fb5e4 | ||
|
ce51821043 | ||
|
eac33d50b3 | ||
|
2552b73b17 | ||
|
8c4019693b | ||
|
b88ef82563 | ||
|
3e5b4a1735 | ||
|
41a7125370 | ||
|
c40484abb1 | ||
|
f4bcfee687 | ||
|
6d8eb9d05e | ||
|
76f2f6a5e1 | ||
|
d677b765b2 | ||
|
7035792325 | ||
|
351c2e97ea | ||
|
ddefb2ee16 | ||
|
c3b41a5e8a | ||
|
b16c58bfe1 | ||
|
e6d0a19e8f | ||
|
2296d09e92 | ||
|
69af252844 | ||
|
d9c598af3c | ||
|
3cb79fc2b5 | ||
|
39379fe5b6 | ||
|
1375f91e15 | ||
|
08a7055679 | ||
|
9f9a828294 | ||
|
77fbb2a851 | ||
|
c0683586e2 | ||
|
86a483f288 | ||
|
3617a3b37d | ||
|
24634aabd7 | ||
|
ffc286c9f9 | ||
|
7064442a4c | ||
|
68a64e9498 | ||
|
b04240b9e7 | ||
|
196a5aea13 | ||
|
f2f99b429f | ||
|
561bda71a9 | ||
|
6b686a96e7 | ||
|
dbe21cc5a7 | ||
|
8b154054c3 | ||
|
972e517280 | ||
|
ca9760cd36 | ||
|
75894fd5bc | ||
|
083a44df2e | ||
|
1e57533127 | ||
|
92e217d0ac | ||
|
707354348a | ||
|
f0d493b064 | ||
|
46f0e6c84d | ||
|
1dd768545a | ||
|
787c8c566f | ||
|
fd10861065 | ||
|
0812ca2927 | ||
|
a8a375ed1f | ||
|
f4b32a2bc6 | ||
|
e2350a65b1 | ||
|
437833cd32 | ||
|
1df524ae6e | ||
|
9d1693cf78 | ||
|
5524e9b9c8 | ||
|
2d6cc3c98e | ||
|
f4b955ccff | ||
|
850473ce40 | ||
|
22d900def1 | ||
|
41eef0c3e2 | ||
|
037fd6e4f7 | ||
|
0b927ddfbb | ||
|
56b8b7c64e | ||
|
0407dbb09b | ||
|
d6932cbb5e | ||
|
c77484e97b | ||
|
9f90cd2635 | ||
|
8fd9e3f312 | ||
|
5df1338046 | ||
|
5d7e0d67cc | ||
|
fdebbac2df | ||
|
a561a9d98a | ||
|
3ac6690ac9 | ||
|
e1f83a50ae | ||
|
8cdc273dba | ||
|
eada9053ad | ||
|
5867774bee | ||
|
18151d9a8e | ||
|
f207a1909f | ||
|
e713d0df9c | ||
|
1ca8a4f4c3 | ||
|
9349425ecd | ||
|
38f6be2aa0 | ||
|
d0abd8c295 | ||
|
9be23cf222 | ||
|
90508a191d | ||
|
10d7d3cb3d | ||
|
89850420f6 | ||
|
9b73eab07c | ||
|
6897e233d4 | ||
|
acfadc8993 | ||
|
cc81e20206 | ||
|
92303c7b26 | ||
|
2866185349 | ||
|
b9570ac6b0 | ||
|
2a3ae641ab | ||
|
d149490c78 | ||
|
c0d8a2c4fb | ||
|
5b03859467 | ||
|
a661634194 | ||
|
c73d45fc07 | ||
|
85a53d7470 | ||
|
18eea191ed | ||
|
14cff9ea44 | ||
|
1696903b8b | ||
|
13c67c9a40 | ||
|
2dcb81d93c | ||
|
b39a9a5edc | ||
|
325242e3aa | ||
|
757d2a4fd9 | ||
|
725ab74e5c | ||
|
b7ca5d330b | ||
|
4034bea6d1 | ||
|
6751c4d2fe | ||
|
53a55d4946 | ||
|
79338d0d75 | ||
|
17bc6a1512 | ||
|
6543ebcd4d | ||
|
351bec6019 | ||
|
a4715bfc3b | ||
|
4ef8030a93 | ||
|
7bfbe81f61 | ||
|
81d6bcf00a | ||
|
a93a039df3 | ||
|
d0d0d2a7c3 | ||
|
6f71439edd | ||
|
b0d3ba7e70 | ||
|
a265b027cc | ||
|
859e6720bf | ||
|
b7bae1120b | ||
|
4727a83d84 | ||
|
67641acdff | ||
|
09b74aa61d | ||
|
d75d266376 | ||
|
645d412c02 | ||
|
5f1609577e | ||
|
351580d9d8 | ||
|
9c9db978c6 | ||
|
a18b4184f5 | ||
|
a7b4f7d66b | ||
|
18933d5075 | ||
|
d53abc661c | ||
|
3481ec8aa2 | ||
|
e563230f10 | ||
|
28f85e3bea | ||
|
4cb4d5ba21 | ||
|
be478561b6 | ||
|
608fdb3ac3 | ||
|
74d9946db3 | ||
|
521e5b22aa | ||
|
85312f6995 | ||
|
f463eb9db1 | ||
|
f4cdfa91c1 | ||
|
fe6ecb1dcf | ||
|
0bf2a5333a | ||
|
105a5b2a31 | ||
|
ecf2f2b9ea | ||
|
493834fcbf | ||
|
fb518715ce | ||
|
fce24aa74d | ||
|
0c30b9ca66 | ||
|
03aa81450e | ||
|
6c34535155 | ||
|
5bd62fe942 | ||
|
26781d2c38 | ||
|
18e1bb33de | ||
|
3a6468897f | ||
|
97484eea6a | ||
|
a2f87f20b7 | ||
|
b6ba08813d | ||
|
3341728eb2 | ||
|
6b172d4c27 | ||
|
4962762c28 | ||
|
56b559894c | ||
|
a9a68a5cc8 | ||
|
542ca52d49 | ||
|
7d0c075b36 | ||
|
c056be3e48 | ||
|
f3e6f99696 | ||
|
3ad4010526 | ||
|
07c07ccb5e | ||
|
6a8f8bf7b7 | ||
|
4917bc228c | ||
|
b58082b536 | ||
|
f70c039275 | ||
|
f102b4fb68 | ||
|
c003f699c7 | ||
|
ad78ec838b | ||
|
34a7d14557 | ||
|
f5b6b43bef | ||
|
3be3794311 | ||
|
710c65aa42 | ||
|
7d3712a4b1 | ||
|
d6955922e9 | ||
|
352d49ec5a | ||
|
88616853c4 | ||
|
8eb7dc0d76 | ||
|
4cc3f97569 | ||
|
e6545f9dca | ||
|
7b1b5e94cb | ||
|
eceb84b978 | ||
|
ffac434bcc | ||
|
e74e5061d8 | ||
|
cb523e7e34 | ||
|
da12c09fda | ||
|
e04e6d2fae | ||
|
e555f67c8d | ||
|
822669fc0c | ||
|
828bc299b2 | ||
|
d22e489626 | ||
|
ab8bc44849 | ||
|
4f832e1eb1 | ||
|
9add9edcc1 | ||
|
59e23fb32a | ||
|
22dcef33da | ||
|
866c598809 | ||
|
b6d99abcb0 | ||
|
c692ac8bab | ||
|
43d064733f | ||
|
6c00aea91d | ||
|
ddff5f923d | ||
|
17e72e0cf4 | ||
|
c25be195f9 | ||
|
d842d395d0 | ||
|
6b2f5944ef | ||
|
9afa707d54 | ||
|
33a725606d | ||
|
e3f13bf545 | ||
|
dbc80a4360 | ||
|
4b63c20a58 | ||
|
8fc0cadae4 | ||
|
52bdc79f79 | ||
|
4c46dd222e | ||
|
6a81bcbefc | ||
|
f42f45e42e | ||
|
333ce37c97 | ||
|
645fc7210e | ||
|
ff37850fea | ||
|
57c24953be | ||
|
92d9435bba | ||
|
9f6bd64100 | ||
|
e86d5cb3f1 | ||
|
f818bdf40e | ||
|
246732153c | ||
|
92ce378a93 | ||
|
4506037bda | ||
|
d99002ef2f | ||
|
a98dcea6fe | ||
|
1e2d2b3d25 | ||
|
0f4546db47 | ||
|
d0b1034f35 | ||
|
1cfe10105a | ||
|
7739923273 | ||
|
e771099568 | ||
|
a361fbab14 | ||
|
61d1685e84 | ||
|
d2267dbd24 | ||
|
87186d73b7 | ||
|
e76111c2b7 | ||
|
60b2caf41d | ||
|
89f3c35466 | ||
|
55d68bd105 | ||
|
7293d47234 | ||
|
49e0088f72 | ||
|
37331c4dc8 | ||
|
562c257596 | ||
|
f481a9baeb | ||
|
322eb15bb5 | ||
|
6b4e51b3a3 | ||
|
daa352d126 | ||
|
e903cbee68 | ||
|
920fd9344f | ||
|
463803f74d | ||
|
7a646d3a84 | ||
|
14a7142ad7 | ||
|
66bea97e8c | ||
|
7214101cfc | ||
|
81f3dc9a2b | ||
|
c7e9235db3 | ||
|
2f010584ef | ||
|
4a50af333d | ||
|
c89a2346a8 | ||
|
22f793a16f | ||
|
6edd486b4c | ||
|
8a6823b78d | ||
|
d80b2935fc | ||
|
d5ae471d94 | ||
|
9a265f7956 | ||
|
04b8394009 | ||
|
6ae4db460e | ||
|
768dfb369c | ||
|
0d173d4c35 | ||
|
4b8bc76622 | ||
|
1a1bfd9348 | ||
|
91bf8cc241 | ||
|
3c7fe00130 | ||
|
5f8ca89772 | ||
|
f855b572e8 | ||
|
5e365bb974 | ||
|
85c42db355 | ||
|
a81c486e0e | ||
|
e8d43f2087 | ||
|
5abb84eda0 | ||
|
9fd7ccc137 | ||
|
a3a617171a | ||
|
47731ce901 | ||
|
fcf57abbb2 | ||
|
3aa8465640 | ||
|
2a8688b54e | ||
|
aec9aa908c | ||
|
8de2cdb1d9 | ||
|
2c1dedfe8a | ||
|
d732778f65 | ||
|
6eb5db0239 | ||
|
7a851084c6 | ||
|
9b6a92c506 | ||
|
781bf985c3 | ||
|
87a142cc30 | ||
|
4fa37bb356 | ||
|
514aabb838 | ||
|
3cb53448c5 | ||
|
8ae0dbcf46 | ||
|
ffcad85d95 | ||
|
14b680f6be | ||
|
74f48d28a4 | ||
|
cab721923a | ||
|
dcd1026a98 | ||
|
31c3b495bf | ||
|
a8b6389092 | ||
|
5e91b95cb3 | ||
|
1ed4c27420 | ||
|
57f825b0a8 | ||
|
846a008c76 | ||
|
a189156f26 | ||
|
d63c0ea262 | ||
|
f74a35074a | ||
|
9a41ab8bfc | ||
|
945d60bd5d | ||
|
892ae3a0d3 | ||
|
2727ba0078 | ||
|
c12472857b | ||
|
385111a1f2 | ||
|
89e89a73dc | ||
|
2fecadd2a6 | ||
|
373ddf9f34 | ||
|
a3fd22b3dd | ||
|
e5e132f67a | ||
|
31b36f23e7 |
18
.gitattributes
vendored
18
.gitattributes
vendored
@@ -1,4 +1,14 @@
|
||||
tests/ export-ignore
|
||||
phpunit.xml export-ignore
|
||||
build.xml export-ignore
|
||||
test export-ignore
|
||||
* text=auto
|
||||
|
||||
/examples export-ignore
|
||||
/tests export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.travis.yml export-ignore
|
||||
.travis.yml export-ignore
|
||||
.scrutinizer.yml export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/CHANGELOG.md export-ignore
|
||||
/CONTRIBUTING.md export-ignore
|
||||
/README.md export-ignore
|
||||
|
||||
|
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,6 +1,9 @@
|
||||
/vendor/
|
||||
/vendor
|
||||
/composer.lock
|
||||
/docs/build/
|
||||
/build/logs/
|
||||
/build/coverage/
|
||||
test
|
||||
phpunit.xml
|
||||
.idea
|
||||
/examples/vendor
|
||||
examples/public.key
|
||||
examples/private.key
|
||||
build
|
||||
*.orig
|
||||
|
35
.scrutinizer.yml
Normal file
35
.scrutinizer.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
filter:
|
||||
excluded_paths:
|
||||
- tests/*
|
||||
- vendor/*
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
remove_extra_empty_lines: true
|
||||
remove_php_closing_tag: true
|
||||
remove_trailing_whitespace: true
|
||||
fix_use_statements:
|
||||
remove_unused: true
|
||||
preserve_multiple: false
|
||||
preserve_blanklines: true
|
||||
order_alphabetically: true
|
||||
fix_php_opening_tag: true
|
||||
fix_linefeed: true
|
||||
fix_line_ending: true
|
||||
fix_identation_4spaces: true
|
||||
fix_doc_comments: true
|
||||
tools:
|
||||
external_code_coverage:
|
||||
timeout: 1800
|
||||
php_code_coverage: false
|
||||
php_code_sniffer:
|
||||
config:
|
||||
standard: PSR2
|
||||
filter:
|
||||
paths: ['src']
|
||||
php_loc:
|
||||
enabled: true
|
||||
excluded_dirs: [vendor, tests, examples]
|
||||
php_cpd:
|
||||
enabled: true
|
||||
excluded_dirs: [vendor, tests, examples]
|
52
.styleci.yml
Normal file
52
.styleci.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
preset: psr2
|
||||
|
||||
enabled:
|
||||
- binary_operator_spaces
|
||||
- blank_line_before_return
|
||||
- concat_with_spaces
|
||||
- fully_qualified_strict_types
|
||||
- function_typehint_space
|
||||
- hash_to_slash_comment
|
||||
- include
|
||||
- lowercase_cast
|
||||
- method_separation
|
||||
- native_function_casing
|
||||
- no_blank_lines_after_class_opening
|
||||
- no_blank_lines_between_uses
|
||||
- no_duplicate_semicolons
|
||||
- no_leading_import_slash
|
||||
- no_leading_namespace_whitespace
|
||||
- no_multiline_whitespace_before_semicolons
|
||||
- no_php4_constructor
|
||||
- no_short_bool_cast
|
||||
- no_singleline_whitespace_before_semicolons
|
||||
- no_trailing_comma_in_singleline_array
|
||||
- no_unreachable_default_argument_value
|
||||
- no_unused_imports
|
||||
- no_whitespace_before_comma_in_array
|
||||
- ordered_imports
|
||||
- phpdoc_align
|
||||
- phpdoc_indent
|
||||
- phpdoc_inline_tag
|
||||
- phpdoc_no_access
|
||||
- phpdoc_no_simplified_null_return
|
||||
- phpdoc_property
|
||||
- phpdoc_scalar
|
||||
- phpdoc_separation
|
||||
- phpdoc_to_comment
|
||||
- phpdoc_trim
|
||||
- phpdoc_type_to_var
|
||||
- phpdoc_types
|
||||
- phpdoc_var_without_name
|
||||
- print_to_echo
|
||||
- short_array_syntax
|
||||
- short_scalar_cast
|
||||
- single_quote
|
||||
- spaces_cast
|
||||
- standardize_not_equal
|
||||
- ternary_operator_spaces
|
||||
- trailing_comma_in_multiline_array
|
||||
- trim_array_spaces
|
||||
- unary_operator_spaces
|
||||
- whitespace_after_comma_in_array
|
||||
- whitespacy_lines
|
34
.travis.yml
34
.travis.yml
@@ -1,8 +1,32 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
before_script: composer install
|
||||
script: phpunit -c build/phpunit.xml
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
|
||||
env:
|
||||
- DEPENDENCIES=""
|
||||
- DEPENDENCIES="--prefer-lowest --prefer-stable"
|
||||
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
install:
|
||||
- composer update --no-interaction --prefer-dist $DEPENDENCIES
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover=coverage.clover
|
||||
- vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests
|
||||
|
||||
after_script:
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
535
CHANGELOG.md
Normal file
535
CHANGELOG.md
Normal file
@@ -0,0 +1,535 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- Clients are now explicitly prevented from using the Client Credentials grant unless they are confidential to conform
|
||||
with the OAuth2 spec (PR #1035)
|
||||
|
||||
## [8.0.0] - released 2019-07-13
|
||||
|
||||
### Added
|
||||
- Flag, `requireCodeChallengeForPublicClients`, used to reject public clients that do not provide a code challenge for the Auth Code Grant; use AuthCodeGrant::disableRequireCodeCallengeForPublicClients() to turn off this requirement (PR #938)
|
||||
- Public clients can now use the Auth Code Grant (PR #938)
|
||||
- `isConfidential` getter added to `ClientEntity` to identify type of client (PR #938)
|
||||
- Function `validateClient()` added to validate clients which was previously performed by the `getClientEntity()` function (PR #938)
|
||||
- Add a new function to the AbstractGrant class called `getClientEntityOrFail()`. This is a wrapper around the `getClientEntity()` function that ensures we emit and throw an exception if the repo doesn't return a client entity. (PR #1010)
|
||||
|
||||
### Changed
|
||||
- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility; AccessTokenEntityInterface now requires `setPrivateKey(CryptKey $privateKey)` so `__toString()` has everything it needs to work (PR #874)
|
||||
- The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899)
|
||||
- `issueAccessToken()` in the Abstract Grant no longer sets access token client, user ID or scopes. These values should already have been set when calling `getNewToken()` (PR #919)
|
||||
- No longer need to enable PKCE with `enableCodeExchangeProof` flag. Any client sending a code challenge will initiate PKCE checks. (PR #938)
|
||||
- Function `getClientEntity()` no longer performs client validation (PR #938)
|
||||
- Password Grant now returns an invalid_grant error instead of invalid_credentials if a user cannot be validated (PR #967)
|
||||
- Use `DateTimeImmutable()` instead of `DateTime()`, `time()` instead of `(new DateTime())->getTimeStamp()`, and `DateTime::getTimeStamp()` instead of `DateTime::format('U')` (PR #963)
|
||||
|
||||
### Removed
|
||||
- `enableCodeExchangeProof` flag (PR #938)
|
||||
- Support for PHP 7.0 (PR #1014)
|
||||
- Remove JTI claim from JWT header (PR #1031)
|
||||
|
||||
## [7.4.0] - released 2019-05-05
|
||||
|
||||
### Changed
|
||||
- RefreshTokenRepository can now return null, allowing refresh tokens to be optional. (PR #649)
|
||||
|
||||
## [7.3.3] - released 2019-03-29
|
||||
|
||||
### Added
|
||||
- Added `error_description` to the error payload to improve standards compliance. The contents of this are copied from the existing `message` value. (PR #1006)
|
||||
|
||||
### Deprecated
|
||||
- Error payload will not issue `message` value in the next major release (PR #1006)
|
||||
|
||||
## [7.3.2] - released 2018-11-21
|
||||
|
||||
### Fixed
|
||||
- Revert setting keys on response type to be inside `getResponseType()` function instead of AuthorizationServer constructor (PR #969)
|
||||
|
||||
## [7.3.1] - released 2018-11-15
|
||||
|
||||
### Fixed
|
||||
- Fix issue with previous release where interface had changed for the AuthorizationServer. Reverted to the previous interface while maintaining functionality changes (PR #970)
|
||||
|
||||
## [7.3.0] - released 2018-11-13
|
||||
|
||||
### Changed
|
||||
- Moved the `finalizeScopes()` call from `validateAuthorizationRequest` method to the `completeAuthorizationRequest` method so it is called just before the access token is issued (PR #923)
|
||||
|
||||
### Added
|
||||
- Added a ScopeTrait to provide an implementation for jsonSerialize (PR #952)
|
||||
- Ability to nest exceptions (PR #965)
|
||||
|
||||
### Fixed
|
||||
- Fix issue where AuthorizationServer is not stateless as ResponseType could store state of a previous request (PR #960)
|
||||
|
||||
## [7.2.0] - released 2018-06-23
|
||||
|
||||
### Changed
|
||||
- Added new`validateRedirectUri` method AbstractGrant to remove three instances of code duplication (PR #912)
|
||||
- Allow 640 as a crypt key file permission (PR #917)
|
||||
|
||||
### Added
|
||||
- Function `hasRedirect()` added to `OAuthServerException` (PR #703)
|
||||
|
||||
### Fixed
|
||||
- Catch and handle `BadMethodCallException` from the `verify()` method of the JWT token in the `validateAuthorization` method (PR #904)
|
||||
|
||||
## [4.1.7] - released 2018-06-23
|
||||
|
||||
### Fixed
|
||||
- Ensure `empty()` function call only contains variable to be compatible with PHP 5.4 (PR #918)
|
||||
|
||||
## [7.1.1] - released 2018-05-21
|
||||
|
||||
### Fixed
|
||||
- No longer set a WWW-Authenticate header for invalid clients if the client did not send an Authorization header in the original request (PR #902)
|
||||
|
||||
## [7.1.0] - released 2018-04-22
|
||||
|
||||
### Changed
|
||||
- Changed hint for unsupportedGrantType exception so it no longer references the grant type parameter which isn't always expected (PR #893)
|
||||
- Upgrade PHPStan checks to level 7 (PR #856)
|
||||
|
||||
### Added
|
||||
- Added event emitters for issued access and refresh tokens (PR #860)
|
||||
- Can now use Defuse\Crypto\Key for encryption/decryption of keys which is faster than the Cryto class (PR #812)
|
||||
|
||||
### Removed
|
||||
- Remove paragone/random_compat from dependencies
|
||||
|
||||
## [7.0.0] - released 2018-02-18
|
||||
|
||||
### Added
|
||||
- Use PHPStan for static analysis of code (PR #848)
|
||||
- Enforce stricter static analysis checks and upgrade library dependencies (PR #852)
|
||||
- Provide PHPStan coverage for tests and update PHPUnit (PR #849)
|
||||
- Get and set methods for OAuth Server Exception payloads. Allow implementer to specify the JSON encode options (PR #719)
|
||||
|
||||
### Changed
|
||||
- ClientRepository interface will now accept null for the Grant type to improve extensibility options (PR #607)
|
||||
- Do not issue an error if key file permissions are 400 or 440 (PR #839)
|
||||
- Skip key file creation if the file already exists (PR #845)
|
||||
- Change changelog format and update readme
|
||||
|
||||
### Removed
|
||||
- Support for PHP 5.6
|
||||
- Support for version 5.x and 6.x of the library
|
||||
|
||||
### Fixed
|
||||
- PKCE implementation (PR #744)
|
||||
- Set correct redirect URI when validating scopes (PR #840)
|
||||
- S256 code challenege method (PR #842)
|
||||
- Accept RSA key with CRLF line endings (PR #805)
|
||||
|
||||
## [6.1.1] - 2017-12-23
|
||||
|
||||
- Removed check on empty scopes
|
||||
|
||||
## [6.1.0] - 2017-12-23
|
||||
|
||||
- Changed the token type issued by the Implicit Grant to be Bearer instead of bearer. (PR #724)
|
||||
- Replaced call to array_key_exists() with the faster isset() on the Implicit Grant. (PR #749)
|
||||
- Allow specification of query delimiter character in the Password Grant (PR #801)
|
||||
- Add Zend Diactoros library dependency to examples (PR #678)
|
||||
- Can set default scope for the authorization endpoint. If no scope is passed during an authorization request, the default scope will be used if set. If not, the server will issue an invalid scope exception (PR #811)
|
||||
- Added validation for redirect URIs on the authorization end point to ensure exactly one redirection URI has been passed (PR #573)
|
||||
|
||||
## [6.0.2] - 2017-08-03
|
||||
|
||||
- An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759)
|
||||
- Removed chmod from CryptKey and add toggle to disable checking (Issue #776)
|
||||
- Fixes invalid code challenge method payload key name (Issue #777)
|
||||
|
||||
## [6.0.1] - 2017-07-19
|
||||
|
||||
To address feedback from the security release the following change has been made:
|
||||
|
||||
- If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception.
|
||||
|
||||
## [6.0.0] - 2017-07-01
|
||||
|
||||
- Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key
|
||||
- Remove support for HHVM
|
||||
- Remove support for PHP 5.5
|
||||
|
||||
## [5.1.4] - 2017-07-01
|
||||
|
||||
- Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater.
|
||||
- It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples.
|
||||
- TravisCI now tests PHP 7.1 (Issue #671)
|
||||
- Fix middleware example fatal error (Issue #682)
|
||||
- Fix typo in the first README sentence (Issue #690)
|
||||
- Corrected DateInterval from 1 min to 1 month (Issue #709)
|
||||
|
||||
## [5.1.3] - 2016-10-12
|
||||
|
||||
- Fixed WWW-Authenticate header (Issue #669)
|
||||
- Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668)
|
||||
|
||||
## [5.1.2] - 2016-09-19
|
||||
|
||||
- Fixed `finalizeScopes` call (Issue #650)
|
||||
|
||||
## [4.1.6] - 2016-09-13
|
||||
|
||||
- Less restrictive on Authorization header check (Issue #652)
|
||||
|
||||
## [5.1.1] - 2016-07-26
|
||||
|
||||
- Improved test suite (Issue #614)
|
||||
- Updated docblocks (Issue #616)
|
||||
- Replace `array_shift` with `foreach` loop (Issue #621)
|
||||
- Allow easy addition of custom fields to Bearer token response (Issue #624)
|
||||
- Key file auto-generation from string (Issue #625)
|
||||
|
||||
## [5.1.0] - 2016-06-28
|
||||
|
||||
- Implemented RFC7636 (Issue #574)
|
||||
- Unify middleware exception responses (Issue #578)
|
||||
- Updated examples (Issue #589)
|
||||
- Ensure state is in access denied redirect (Issue #597)
|
||||
- Remove redundant `isExpired()` method from entity interfaces and traits (Issue #600)
|
||||
- Added a check for unique access token constraint violation (Issue #601)
|
||||
- Look at Authorization header directly for HTTP Basic auth checks (Issue #604)
|
||||
- Added catch Runtime exception when parsing JWT string (Issue #605)
|
||||
- Allow `paragonie/random_compat` 2.x (Issue #606)
|
||||
- Added `indigophp/hash-compat` to Composer suggestions and `require-dev` for PHP 5.5 support
|
||||
|
||||
## [5.0.3] - 2016-05-04
|
||||
|
||||
- Fix hints in PasswordGrant (Issue #560)
|
||||
- Add meaning of `Resource owner` to terminology.md (Issue #561)
|
||||
- Use constant for event name instead of explicit string (Issue #563)
|
||||
- Remove unused request property (Issue #564)
|
||||
- Correct wrong phpdoc (Issue #569)
|
||||
- Fixed typo in exception string (Issue #570)
|
||||
|
||||
## [5.0.2] - 2016-04-18
|
||||
|
||||
- `state` parameter is now correctly returned after implicit grant authorization
|
||||
- Small code and docblock improvements
|
||||
|
||||
## [5.0.1] - 2016-04-18
|
||||
|
||||
- Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
|
||||
|
||||
## [5.0.0] - 2016-04-17
|
||||
|
||||
Version 5 is a complete code rewrite.
|
||||
|
||||
- Renamed Server class to AuthorizationServer
|
||||
- Added ResourceServer class
|
||||
- Run unit tests again PHP 5.5.9 as it's the minimum supported version
|
||||
- Enable PHPUnit 5.0 support
|
||||
- Improved examples and documentation
|
||||
- Make it clearer that the implicit grant doesn't support refresh tokens
|
||||
- Improved refresh token validation errors
|
||||
- Fixed refresh token expiry date
|
||||
|
||||
## [5.0.0-RC2] - 2016-04-10
|
||||
|
||||
- Allow multiple client redirect URIs (Issue #511)
|
||||
- Remove unused mac token interface (Issue #503)
|
||||
- Handle RSA key passphrase (Issue #502)
|
||||
- Remove access token repository from response types (Issue #501)
|
||||
- Remove unnecessary methods from entity interfaces (Issue #490)
|
||||
- Ensure incoming JWT hasn't expired (Issue #509)
|
||||
- Fix client identifier passed where user identifier is expected (Issue #498)
|
||||
- Removed built-in entities; added traits to for quick re-use (Issue #504)
|
||||
- Redirect uri is required only if the "redirect_uri" parameter was included in the authorization request (Issue #514)
|
||||
- Removed templating for auth code and implicit grants (Issue #499)
|
||||
|
||||
## [5.0.0-RC1] - 2016-03-24
|
||||
|
||||
Version 5 is a complete code rewrite.
|
||||
|
||||
- JWT support
|
||||
- PSR-7 support
|
||||
- Improved exception errors
|
||||
- Replace all occurrences of the term "Storage" with "Repository"
|
||||
- Simplify repositories
|
||||
- Entities conform to interfaces and use traits
|
||||
- Auth code grant updated
|
||||
- Allow support for public clients
|
||||
- Add support for #439
|
||||
- Client credentials grant updated
|
||||
- Password grant updated
|
||||
- Allow support for public clients
|
||||
- Refresh token grant updated
|
||||
- Implement Implicit grant
|
||||
- Bearer token output type
|
||||
- Remove MAC token output type
|
||||
- Authorization server rewrite
|
||||
- Resource server class moved to PSR-7 middleware
|
||||
- Tests
|
||||
- Much much better documentation
|
||||
|
||||
## [4.1.5] - 2016-01-04
|
||||
|
||||
- Enable Symfony 3.0 support (#412)
|
||||
|
||||
## [4.1.4] - 2015-11-13
|
||||
|
||||
- Fix for determining access token in header (Issue #328)
|
||||
- Refresh tokens are now returned for MAC responses (Issue #356)
|
||||
- Added integration list to readme (Issue #341)
|
||||
- Expose parameter passed to exceptions (Issue #345)
|
||||
- Removed duplicate routing setup code (Issue #346)
|
||||
- Docs fix (Issues #347, #360, #380)
|
||||
- Examples fix (Issues #348, #358)
|
||||
- Fix typo in docblock (Issue #352)
|
||||
- Improved timeouts for MAC tokens (Issue #364)
|
||||
- `hash_hmac()` should output raw binary data, not hexits (Issue #370)
|
||||
- Improved regex for matching all Base64 characters (Issue #371)
|
||||
- Fix incorrect signature parameter (Issue #372)
|
||||
- AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377)
|
||||
- Added priority argument to event listener (Issue #388)
|
||||
|
||||
## [4.1.3] - 2015-03-22
|
||||
|
||||
- Docblock, namespace and inconsistency fixes (Issue #303)
|
||||
- Docblock type fix (Issue #310)
|
||||
- Example bug fix (Issue #300)
|
||||
- Updated league/event to ~2.1 (Issue #311)
|
||||
- Fixed missing session scope (Issue #319)
|
||||
- Updated interface docs (Issue #323)
|
||||
- `.travis.yml` updates
|
||||
|
||||
## [4.1.2] - 2015-01-01
|
||||
|
||||
- Remove side-effects in hash_equals() implementation (Issue #290)
|
||||
|
||||
## [4.1.1] - 2014-12-31
|
||||
|
||||
- Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*`
|
||||
|
||||
## [4.1.0] - 2014-12-27
|
||||
|
||||
- Added MAC token support (Issue #158)
|
||||
- Fixed example init code (Issue #280)
|
||||
- Toggle refresh token rotation (Issue #286)
|
||||
- Docblock fixes
|
||||
|
||||
## [4.0.5] - 2014-12-15
|
||||
|
||||
- Prevent duplicate session in auth code grant (Issue #282)
|
||||
|
||||
## [4.0.4] - 2014-12-03
|
||||
|
||||
- Ensure refresh token hasn't expired (Issue #270)
|
||||
|
||||
## [4.0.3] - 2014-12-02
|
||||
|
||||
- Fix bad type hintings (Issue #267)
|
||||
- Do not forget to set the expire time (Issue #268)
|
||||
|
||||
## [4.0.2] - 2014-11-21
|
||||
|
||||
- Improved interfaces (Issue #255)
|
||||
- Learnt how to spell delimiter and so `getScopeDelimiter()` and `setScopeDelimiter()` methods have been renamed
|
||||
- Docblock improvements (Issue #254)
|
||||
|
||||
## [4.0.1] - 2014-11-09
|
||||
|
||||
- Alias the master branch in composer.json (Issue #243)
|
||||
- Numerous PHP CodeSniffer fixes (Issue #244)
|
||||
- .travis.yml update (Issue #245)
|
||||
- The getAccessToken method should return an AccessTokenEntity object instead of a string in ResourceServer.php (#246)
|
||||
|
||||
## [4.0.0] - 2014-11-08
|
||||
|
||||
- Complete rewrite
|
||||
- Check out the documentation - [http://oauth2.thephpleague.com](http://oauth2.thephpleague.com)
|
||||
|
||||
## [3.2.0] - 2014-04-16
|
||||
|
||||
- Added the ability to change the algorithm that is used to generate the token strings (Issue #151)
|
||||
|
||||
## [3.1.2] - 2014-02-26
|
||||
|
||||
- Support Authorization being an environment variable. [See more](http://fortrabbit.com/docs/essentials/quirks-and-constraints#authorization-header)
|
||||
|
||||
## [3.1.1] - 2013-12-05
|
||||
|
||||
- Normalize headers when `getallheaders()` is available (Issues #108 and #114)
|
||||
|
||||
## [3.1.0] - 2013-12-05
|
||||
|
||||
- No longer necessary to inject the authorisation server into a grant, the server will inject itself
|
||||
- Added test for 1419ba8cdcf18dd034c8db9f7de86a2594b68605
|
||||
|
||||
## [3.0.1] - 2013-12-02
|
||||
|
||||
- Forgot to tell TravisCI from testing PHP 5.3
|
||||
|
||||
## [3.0.0] - 2013-12-02
|
||||
|
||||
- Fixed spelling of Implicit grant class (Issue #84)
|
||||
- Travis CI now tests for PHP 5.5
|
||||
- Fixes for checking headers for resource server (Issues #79 and #)
|
||||
- The word "bearer" now has a capital "B" in JSON output to match OAuth 2.0 spec
|
||||
- All grants no longer remove old sessions by default
|
||||
- All grants now support custom access token TTL (Issue #92)
|
||||
- All methods which didn't before return a value now return `$this` to support method chaining
|
||||
- Removed the build in DB providers - these will be put in their own repos to remove baggage in the main repository
|
||||
- Removed support for PHP 5.3 because this library now uses traits and will use other modern PHP features going forward
|
||||
- Moved some grant related functions into a trait to reduce duplicate code
|
||||
|
||||
## [2.1.1] - 2013-06-02
|
||||
|
||||
- Added conditional `isValid()` flag to check for Authorization header only (thanks @alexmcroberts)
|
||||
- Fixed semantic meaning of `requireScopeParam()` and `requireStateParam()` by changing their default value to true
|
||||
- Updated some duff docblocks
|
||||
- Corrected array key call in Resource.php (Issue #63)
|
||||
|
||||
## [2.1.0] - 2013-05-10
|
||||
|
||||
- Moved zetacomponents/database to "suggest" in composer.json. If you rely on this feature you now need to include " zetacomponents/database" into "require" key in your own composer.json. (Issue #51)
|
||||
- New method in Refresh grant called `rotateRefreshTokens()`. Pass in `true` to issue a new refresh token each time an access token is refreshed. This parameter needs to be set to true in order to request reduced scopes with the new access token. (Issue #47)
|
||||
- Rename `key` column in oauth_scopes table to `scope` as `key` is a reserved SQL word. (Issue #45)
|
||||
- The `scope` parameter is no longer required by default as per the RFC. (Issue #43)
|
||||
- You can now set multiple default scopes by passing an array into `setDefaultScope()`. (Issue #42)
|
||||
- The password and client credentials grants now allow for multiple sessions per user. (Issue #32)
|
||||
- Scopes associated to authorization codes are not held in their own table (Issue #44)
|
||||
- Database schema updates.
|
||||
|
||||
## [2.0.5] - 2013-05-09
|
||||
|
||||
- Fixed `oauth_session_token_scopes` table primary key
|
||||
- Removed `DEFAULT ''` that has slipped into some tables
|
||||
- Fixed docblock for `SessionInterface::associateRefreshToken()`
|
||||
|
||||
## [2.0.4] - 2013-05-09
|
||||
|
||||
- Renamed primary key in oauth_client_endpoints table
|
||||
- Adding missing column to oauth_session_authcodes
|
||||
|
||||
### Security
|
||||
- A refresh token should be bound to a client ID
|
||||
|
||||
## [2.0.3] - 2013-05-08
|
||||
|
||||
- Fixed a link to code in composer.json
|
||||
|
||||
## [2.0.2] - 2013-05-08
|
||||
|
||||
- Updated README with wiki guides
|
||||
- Removed `null` as default parameters in some methods in the storage interfaces
|
||||
- Fixed license copyright
|
||||
|
||||
## [2.0.0] - 2013-05-08
|
||||
|
||||
**If you're upgrading from v1.0.8 there are lots of breaking changes**
|
||||
|
||||
- Rewrote the session storage interface from scratch so methods are more obvious
|
||||
- Included a PDO driver which implements the storage interfaces so the library is more "get up and go"
|
||||
- Further normalised the database structure so all sessions no longer contain infomation related to authorization grant (which may or may not be enabled)
|
||||
- A session can have multiple associated access tokens
|
||||
- Individual grants can have custom expire times for access tokens
|
||||
- Authorization codes now have a TTL of 10 minutes by default (can be manually set)
|
||||
- Refresh tokens now have a TTL of one week by default (can be manually set)
|
||||
- The client credentials grant will no longer gives out refresh tokens as per the specification
|
||||
|
||||
## [1.0.8] - 2013-03-18
|
||||
|
||||
- Fixed check for required state parameter
|
||||
- Fixed check that user's credentials are correct in Password grant
|
||||
|
||||
## [1.0.7] - 2013-03-04
|
||||
|
||||
- Added method `requireStateParam()`
|
||||
- Added method `requireScopeParam()`
|
||||
|
||||
## [1.0.6] - 2013-02-22
|
||||
|
||||
- Added links to tutorials in the README
|
||||
- Added missing `state` parameter request to the `checkAuthoriseParams()` method.
|
||||
|
||||
## [1.0.5] - 2013-02-21
|
||||
|
||||
- Fixed the SQL example for SessionInterface::getScopes()
|
||||
|
||||
## [1.0.3] - 2013-02-20
|
||||
|
||||
- Changed all instances of the "authentication server" to "authorization server"
|
||||
|
||||
## [1.0.2] - 2013-02-20
|
||||
|
||||
- Fixed MySQL create table order
|
||||
- Fixed version number in composer.json
|
||||
|
||||
## [1.0.1] - 2013-02-19
|
||||
|
||||
- Updated AuthServer.php to use `self::getParam()`
|
||||
|
||||
## 1.0.0 - 2013-02-15
|
||||
|
||||
- First major release
|
||||
|
||||
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.0.0...HEAD
|
||||
[8.0.0]: https://github.com/thephpleague/oauth2-server/compare/7.4.0...8.0.0
|
||||
[7.4.0]: https://github.com/thephpleague/oauth2-server/compare/7.3.3...7.4.0
|
||||
[7.3.3]: https://github.com/thephpleague/oauth2-server/compare/7.3.2...7.3.3
|
||||
[7.3.2]: https://github.com/thephpleague/oauth2-server/compare/7.3.1...7.3.2
|
||||
[7.3.1]: https://github.com/thephpleague/oauth2-server/compare/7.3.0...7.3.1
|
||||
[7.3.0]: https://github.com/thephpleague/oauth2-server/compare/7.2.0...7.3.0
|
||||
[7.2.0]: https://github.com/thephpleague/oauth2-server/compare/7.1.1...7.2.0
|
||||
[7.1.1]: https://github.com/thephpleague/oauth2-server/compare/7.1.0...7.1.1
|
||||
[7.1.0]: https://github.com/thephpleague/oauth2-server/compare/7.0.0...7.1.0
|
||||
[7.0.0]: https://github.com/thephpleague/oauth2-server/compare/6.1.1...7.0.0
|
||||
[6.1.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.1.1
|
||||
[6.1.0]: https://github.com/thephpleague/oauth2-server/compare/6.0.2...6.1.0
|
||||
[6.0.2]: https://github.com/thephpleague/oauth2-server/compare/6.0.1...6.0.2
|
||||
[6.0.1]: https://github.com/thephpleague/oauth2-server/compare/6.0.0...6.0.1
|
||||
[6.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.1.4...6.0.0
|
||||
[5.1.4]: https://github.com/thephpleague/oauth2-server/compare/5.1.3...5.1.4
|
||||
[5.1.3]: https://github.com/thephpleague/oauth2-server/compare/5.1.2...5.1.3
|
||||
[5.1.2]: https://github.com/thephpleague/oauth2-server/compare/5.1.1...5.1.2
|
||||
[5.1.1]: https://github.com/thephpleague/oauth2-server/compare/5.1.0...5.1.1
|
||||
[5.1.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.2...5.1.0
|
||||
[5.0.3]: https://github.com/thephpleague/oauth2-server/compare/5.0.3...5.0.2
|
||||
[5.0.2]: https://github.com/thephpleague/oauth2-server/compare/5.0.1...5.0.2
|
||||
[5.0.1]: https://github.com/thephpleague/oauth2-server/compare/5.0.0...5.0.1
|
||||
[5.0.0]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC2...5.0.0
|
||||
[5.0.0-RC2]: https://github.com/thephpleague/oauth2-server/compare/5.0.0-RC1...5.0.0-RC2
|
||||
[5.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...5.0.0-RC1
|
||||
[4.1.7]: https://github.com/thephpleague/oauth2-server/compare/4.1.6...4.1.7
|
||||
[4.1.6]: https://github.com/thephpleague/oauth2-server/compare/4.1.5...4.1.6
|
||||
[4.1.5]: https://github.com/thephpleague/oauth2-server/compare/4.1.4...4.1.5
|
||||
[4.1.4]: https://github.com/thephpleague/oauth2-server/compare/4.1.3...4.1.4
|
||||
[4.1.3]: https://github.com/thephpleague/oauth2-server/compare/4.1.2...4.1.3
|
||||
[4.1.2]: https://github.com/thephpleague/oauth2-server/compare/4.1.1...4.1.2
|
||||
[4.1.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.1.1
|
||||
[4.1.0]: https://github.com/thephpleague/oauth2-server/compare/4.0.5...4.1.0
|
||||
[4.0.5]: https://github.com/thephpleague/oauth2-server/compare/4.0.4...4.0.5
|
||||
[4.0.4]: https://github.com/thephpleague/oauth2-server/compare/4.0.3...4.0.4
|
||||
[4.0.3]: https://github.com/thephpleague/oauth2-server/compare/4.0.2...4.0.3
|
||||
[4.0.2]: https://github.com/thephpleague/oauth2-server/compare/4.0.1...4.0.2
|
||||
[4.0.1]: https://github.com/thephpleague/oauth2-server/compare/4.0.0...4.0.1
|
||||
[4.0.0]: https://github.com/thephpleague/oauth2-server/compare/3.2.0...4.0.0
|
||||
[3.2.0]: https://github.com/thephpleague/oauth2-server/compare/3.1.2...3.2.0
|
||||
[3.1.2]: https://github.com/thephpleague/oauth2-server/compare/3.1.1...3.1.2
|
||||
[3.1.1]: https://github.com/thephpleague/oauth2-server/compare/3.1.0...3.1.1
|
||||
[3.1.0]: https://github.com/thephpleague/oauth2-server/compare/3.0.1...3.1.0
|
||||
[3.0.1]: https://github.com/thephpleague/oauth2-server/compare/3.0.0...3.0.1
|
||||
[3.0.0]: https://github.com/thephpleague/oauth2-server/compare/2.1.1...3.0.0
|
||||
[2.1.1]: https://github.com/thephpleague/oauth2-server/compare/2.1.0...2.1.1
|
||||
[2.1.0]: https://github.com/thephpleague/oauth2-server/compare/2.0.5...2.1.0
|
||||
[2.0.5]: https://github.com/thephpleague/oauth2-server/compare/2.0.4...2.0.5
|
||||
[2.0.4]: https://github.com/thephpleague/oauth2-server/compare/2.0.3...2.0.4
|
||||
[2.0.3]: https://github.com/thephpleague/oauth2-server/compare/2.0.2...2.0.3
|
||||
[2.0.2]: https://github.com/thephpleague/oauth2-server/compare/2.0.0...2.0.2
|
||||
[2.0.0]: https://github.com/thephpleague/oauth2-server/compare/1.0.8...2.0.0
|
||||
[1.0.8]: https://github.com/thephpleague/oauth2-server/compare/1.0.7...1.0.8
|
||||
[1.0.7]: https://github.com/thephpleague/oauth2-server/compare/1.0.6...1.0.7
|
||||
[1.0.6]: https://github.com/thephpleague/oauth2-server/compare/1.0.5...1.0.6
|
||||
[1.0.5]: https://github.com/thephpleague/oauth2-server/compare/1.0.3...1.0.5
|
||||
[1.0.3]: https://github.com/thephpleague/oauth2-server/compare/1.0.2...1.0.3
|
||||
[1.0.2]: https://github.com/thephpleague/oauth2-server/compare/1.0.1...1.0.2
|
||||
[1.0.1]: https://github.com/thephpleague/oauth2-server/compare/1.0.0...1.0.1
|
73
CODE_OF_CONDUCT.md
Normal file
73
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
education, socio-economic status, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at andrew@noexceptions.io. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
15
CONTRIBUTING.md
Normal file
15
CONTRIBUTING.md
Normal file
@@ -0,0 +1,15 @@
|
||||
Thanks for contributing to this project.
|
||||
|
||||
|
||||
**Please submit your pull request against the `master` branch only.**
|
||||
|
||||
|
||||
Please ensure that you run `phpunit` from the project root after you've made any changes.
|
||||
|
||||
If you've added something new please create a new unit test, if you've changed something please update any unit tests as appropritate.
|
||||
|
||||
We're trying to ensure there is **100%** test code coverage (including testing PHP errors and exceptions) so please ensure any new/updated tests cover all of your changes.
|
||||
|
||||
Thank you,
|
||||
|
||||
@alexbilbie
|
@@ -1,20 +1,20 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2012 University of Lincoln
|
||||
Copyright (C) Alex Bilbie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
114
README.md
114
README.md
@@ -1,52 +1,108 @@
|
||||
# PHP OAuth Framework
|
||||
# PHP OAuth 2.0 Server
|
||||
|
||||
The goal of this project is to develop a standards compliant [OAuth 2](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authentication server, resource server and client library with support for a major OAuth 2 providers.
|
||||
[](https://github.com/thephpleague/oauth2-server/releases)
|
||||
[](LICENSE.md)
|
||||
[](https://travis-ci.org/thephpleague/oauth2-server)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/oauth2-server/code-structure)
|
||||
[](https://scrutinizer-ci.com/g/thephpleague/oauth2-server)
|
||||
[](https://packagist.org/packages/league/oauth2-server)
|
||||
[](https://github.com/phpstan/phpstan)
|
||||
|
||||
## Package Installation
|
||||
`league/oauth2-server` is a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them.
|
||||
|
||||
The framework is provided as a Composer package which can be installed by adding the package to your composer.json file:
|
||||
Out of the box it supports the following grants:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"require": {
|
||||
"lncd\Oauth2": "*"
|
||||
}
|
||||
}
|
||||
* Authorization code grant
|
||||
* Implicit grant
|
||||
* Client credentials grant
|
||||
* Resource owner password credentials grant
|
||||
* Refresh grant
|
||||
|
||||
The following RFCs are implemented:
|
||||
|
||||
* [RFC6749 "OAuth 2.0"](https://tools.ietf.org/html/rfc6749)
|
||||
* [RFC6750 " The OAuth 2.0 Authorization Framework: Bearer Token Usage"](https://tools.ietf.org/html/rfc6750)
|
||||
* [RFC7519 "JSON Web Token (JWT)"](https://tools.ietf.org/html/rfc7519)
|
||||
* [RFC7636 "Proof Key for Code Exchange by OAuth Public Clients"](https://tools.ietf.org/html/rfc7636)
|
||||
|
||||
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
|
||||
|
||||
## Requirements
|
||||
|
||||
The following versions of PHP are supported:
|
||||
|
||||
* PHP 7.1
|
||||
* PHP 7.2
|
||||
* PHP 7.3
|
||||
|
||||
The `openssl` and `json` extensions are also required.
|
||||
|
||||
All HTTP messages passed to the server should be [PSR-7 compliant](https://www.php-fig.org/psr/psr-7/). This ensures interoperability with other packages and frameworks.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer require league/oauth2-server
|
||||
```
|
||||
|
||||
## Package Integration
|
||||
## Documentation
|
||||
|
||||
Check out the [wiki](https://github.com/lncd/OAuth2/wiki)
|
||||
The library documentation can be found at [https://oauth2.thephpleague.com](https://oauth2.thephpleague.com).
|
||||
You can contribute to the documentation in the [gh-pages branch](https://github.com/thephpleague/oauth2-server/tree/gh-pages/).
|
||||
|
||||
## Current Features
|
||||
## Testing
|
||||
|
||||
### Authentication Server
|
||||
The library uses [PHPUnit](https://phpunit.de/) for unit tests and [PHPStan](https://github.com/phpstan/phpstan) for static analysis of the code.
|
||||
|
||||
The authentication server is a flexible class that supports the following grants:
|
||||
```
|
||||
vendor/bin/phpunit
|
||||
vendor/bin/phpstan analyse -l 7 -c phpstan.neon src tests
|
||||
```
|
||||
|
||||
* authentication code
|
||||
* refresh token
|
||||
* client credentials
|
||||
* password (user credentials)
|
||||
## Continous Integration
|
||||
|
||||
### Resource Server
|
||||
We use [Travis CI](https://travis-ci.org/), [Scrutinizer](https://scrutinizer-ci.com/), and [StyleCI](https://styleci.io/) for continuous integration. Check out [our](https://github.com/thephpleague/oauth2-server/blob/master/.travis.yml) [configuration](https://github.com/thephpleague/oauth2-server/blob/master/.scrutinizer.yml) [files](https://github.com/thephpleague/oauth2-server/blob/master/.styleci.yml) if you'd like to know more.
|
||||
|
||||
The resource server allows you to secure your API endpoints by checking for a valid OAuth access token in the request and ensuring the token has the correct permission to access resources.
|
||||
## Community Integrations
|
||||
|
||||
* [Drupal](https://www.drupal.org/project/simple_oauth)
|
||||
* [Laravel Passport](https://github.com/laravel/passport)
|
||||
* [OAuth 2 Server for CakePHP 3](https://github.com/uafrica/oauth-server)
|
||||
* [OAuth 2 Server for Expressive](https://github.com/zendframework/zend-expressive-authentication-oauth2)
|
||||
* [Trikoder OAuth 2 Bundle (Symfony)](https://github.com/trikoder/oauth2-bundle)
|
||||
|
||||
## Changelog
|
||||
|
||||
See the [project changelog](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md)
|
||||
|
||||
## Future Goals
|
||||
## Contributing
|
||||
|
||||
### Authentication Server
|
||||
Contributions are always welcome. Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CODE_OF_CONDUCT.md) for details.
|
||||
|
||||
* Support for [JSON web tokens](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-json-web-token/).
|
||||
* Support for [SAML assertions](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-saml2-bearer/).
|
||||
## Support
|
||||
|
||||
### Client support
|
||||
Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues).
|
||||
|
||||
* Merge in https://github.com/philsturgeon/codeigniter-oauth2
|
||||
If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below.
|
||||
|
||||
---
|
||||
## Security
|
||||
|
||||
This code will be developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which has been funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme.
|
||||
If you discover any security related issues, please email `andrew@noexceptions.io` instead of using the issue tracker.
|
||||
|
||||
## License
|
||||
|
||||
This package is released under the MIT License. See the bundled [LICENSE](https://github.com/thephpleague/oauth2-server/blob/master/LICENSE) file for details.
|
||||
|
||||
## Credits
|
||||
|
||||
This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster).
|
||||
|
||||
Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/).
|
||||
|
||||
PHP OAuth 2.0 Server is one of many packages provided by The PHP League. To find out more, please visit [our website](https://thephpleague.com).
|
||||
|
||||
Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors).
|
||||
|
||||
Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library.
|
||||
|
||||
The initial code was developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which was funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme.
|
||||
|
142
build.xml
142
build.xml
@@ -1,142 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="PHP OAuth 2.0 Server" default="build">
|
||||
|
||||
<target name="build" depends="prepare,lint,phploc,pdepend,phpmd-ci,phpcs-ci,phpcpd,composer,phpunit,phpdox,phpcb"/>
|
||||
|
||||
<target name="build-parallel" depends="prepare,lint,tools-parallel,phpcb"/>
|
||||
|
||||
<target name="minimal" depends="prepare,lint,phploc,pdepend,phpcpd,composer,phpunit,phpdox,phpcb" />
|
||||
|
||||
<target name="tools-parallel" description="Run tools in parallel">
|
||||
<parallel threadCount="2">
|
||||
<sequential>
|
||||
<antcall target="pdepend"/>
|
||||
<antcall target="phpmd-ci"/>
|
||||
</sequential>
|
||||
<antcall target="phpcpd"/>
|
||||
<antcall target="phpcs-ci"/>
|
||||
<antcall target="phploc"/>
|
||||
<antcall target="phpdox"/>
|
||||
</parallel>
|
||||
</target>
|
||||
|
||||
<target name="clean" description="Cleanup build artifacts">
|
||||
<delete dir="${basedir}/build/api"/>
|
||||
<delete dir="${basedir}/build/code-browser"/>
|
||||
<delete dir="${basedir}/build/coverage"/>
|
||||
<delete dir="${basedir}/build/logs"/>
|
||||
<delete dir="${basedir}/build/pdepend"/>
|
||||
</target>
|
||||
|
||||
<target name="prepare" depends="clean" description="Prepare for build">
|
||||
<mkdir dir="${basedir}/build/api"/>
|
||||
<mkdir dir="${basedir}/build/code-browser"/>
|
||||
<mkdir dir="${basedir}/build/coverage"/>
|
||||
<mkdir dir="${basedir}/build/logs"/>
|
||||
<mkdir dir="${basedir}/build/pdepend"/>
|
||||
<mkdir dir="${basedir}/build/phpdox"/>
|
||||
</target>
|
||||
|
||||
<target name="lint">
|
||||
<apply executable="php" failonerror="true">
|
||||
<arg value="-l" />
|
||||
|
||||
<fileset dir="${basedir}/src">
|
||||
<include name="**/*.php" />
|
||||
<modified />
|
||||
</fileset>
|
||||
</apply>
|
||||
</target>
|
||||
|
||||
<target name="phploc" description="Measure project size using PHPLOC">
|
||||
<exec executable="phploc">
|
||||
<arg value="--log-csv" />
|
||||
<arg value="${basedir}/build/logs/phploc.csv" />
|
||||
<arg path="${basedir}/src" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="pdepend" description="Calculate software metrics using PHP_Depend">
|
||||
<exec executable="pdepend">
|
||||
<arg value="--jdepend-xml=${basedir}/build/logs/jdepend.xml" />
|
||||
<arg value="--jdepend-chart=${basedir}/build/pdepend/dependencies.svg" />
|
||||
<arg value="--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg" />
|
||||
<arg path="${basedir}/src" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="phpmd" description="Perform project mess detection using PHPMD and print human readable output. Intended for usage on the command line before committing.">
|
||||
<exec executable="phpmd">
|
||||
<arg path="${basedir}/src" />
|
||||
<arg value="text" />
|
||||
<arg value="${basedir}/build/phpmd.xml" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="phpmd-ci" description="Perform project mess detection using PHPMD creating a log file for the continuous integration server">
|
||||
<exec executable="phpmd">
|
||||
<arg path="${basedir}/src" />
|
||||
<arg value="xml" />
|
||||
<arg value="${basedir}/build/phpmd.xml" />
|
||||
<arg value="--reportfile" />
|
||||
<arg value="${basedir}/build/logs/pmd.xml" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="phpcs" description="Find coding standard violations using PHP_CodeSniffer and print human readable output. Intended for usage on the command line before committing.">
|
||||
<exec executable="phpcs">
|
||||
<arg value="--standard=${basedir}/build/phpcs.xml" />
|
||||
<arg value="--extensions=php" />
|
||||
<arg value="--ignore=third_party/CIUnit" />
|
||||
<arg path="${basedir}/src" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="phpcs-ci" description="Find coding standard violations using PHP_CodeSniffer creating a log file for the continuous integration server">
|
||||
<exec executable="phpcs" output="/dev/null">
|
||||
<arg value="--report=checkstyle" />
|
||||
<arg value="--report-file=${basedir}/build/logs/checkstyle.xml" />
|
||||
<arg value="--standard=${basedir}/build/phpcs.xml" />
|
||||
<arg value="--extensions=php" />
|
||||
<arg value="--ignore=third_party/CIUnit" />
|
||||
<arg path="${basedir}/src" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="phpcpd" description="Find duplicate code using PHPCPD">
|
||||
<exec executable="phpcpd">
|
||||
<arg value="--log-pmd" />
|
||||
<arg value="${basedir}/build/logs/pmd-cpd.xml" />
|
||||
<arg path="${basedir}/src" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="composer" description="Install Composer requirements">
|
||||
<exec executable="composer.phar" failonerror="true">
|
||||
<arg value="install" />
|
||||
<arg value="--dev" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="phpunit" description="Run unit tests with PHPUnit">
|
||||
<exec executable="${basedir}/vendor/bin/phpunit" failonerror="true">
|
||||
<arg value="--configuration" />
|
||||
<arg value="${basedir}/build/phpunit.xml" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="phpdox" description="Generate API documentation using phpDox">
|
||||
<exec executable="phpdox"/>
|
||||
</target>
|
||||
|
||||
<target name="phpcb" description="Aggregate tool output with PHP_CodeBrowser">
|
||||
<exec executable="phpcb">
|
||||
<arg value="--log" />
|
||||
<arg path="${basedir}/build/logs" />
|
||||
<arg value="--source" />
|
||||
<arg path="${basedir}/src" />
|
||||
<arg value="--output" />
|
||||
<arg path="${basedir}/build/code-browser" />
|
||||
</exec>
|
||||
</target>
|
||||
</project>
|
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="PHP_CodeSniffer">
|
||||
|
||||
<description>PHP_CodeSniffer configuration</description>
|
||||
|
||||
<rule ref="PSR2"/>
|
||||
|
||||
</ruleset>
|
@@ -1,14 +0,0 @@
|
||||
<ruleset name="OAuth 2.0 Server"
|
||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
|
||||
http://pmd.sf.net/ruleset_xml_schema.xsd"
|
||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
||||
|
||||
<description>
|
||||
Ruleset for OAuth 2.0 server
|
||||
</description>
|
||||
|
||||
<!-- Import the entire unused code rule set -->
|
||||
<rule ref="rulesets/unusedcode.xml" />
|
||||
</ruleset>
|
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false" bootstrap="../tests/Bootstrap.php">
|
||||
<testsuites>
|
||||
<testsuite name="Authentication Server">
|
||||
<directory suffix="test.php">../tests/authentication</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Resource Server">
|
||||
<directory suffix="test.php">../tests/resource</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<blacklist>
|
||||
<directory suffix=".php">PEAR_INSTALL_DIR</directory>
|
||||
<directory suffix=".php">PHP_LIBDIR</directory>
|
||||
<directory suffix=".php">../vendor/composer</directory>
|
||||
<directory suffix=".php">../tests</directory>
|
||||
</blacklist>
|
||||
</filter>
|
||||
<logging>
|
||||
<log type="coverage-html" target="coverage" title="lncd/OAuth" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="90"/>
|
||||
<log type="coverage-text" target="php://stdout" title="lncd/OAuth" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="90"/>
|
||||
<log type="coverage-clover" target="logs/clover.xml"/>
|
||||
<log type="junit" target="logs/junit.xml" logIncompleteSkipped="false"/>
|
||||
</logging>
|
||||
</phpunit>
|
114
composer.json
114
composer.json
@@ -1,44 +1,72 @@
|
||||
{
|
||||
"name": "lncd/oauth2",
|
||||
"description": "OAuth 2.0 Framework",
|
||||
"version": "0.4.2",
|
||||
"homepage": "https://github.com/lncd/OAuth2",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"guzzle/guzzle": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/lncd/OAuth2"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"server",
|
||||
"authorization",
|
||||
"authentication",
|
||||
"resource"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Oauth2": "src/"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"lncd/oauth2-facebook": "Adds support for Facebook as an IDP"
|
||||
}
|
||||
}
|
||||
"name": "league/oauth2-server",
|
||||
"description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.",
|
||||
"homepage": "https://oauth2.thephpleague.com/",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=7.1.0",
|
||||
"ext-openssl": "*",
|
||||
"league/event": "^2.2",
|
||||
"lcobucci/jwt": "^3.3.1",
|
||||
"psr/http-message": "^1.0.1",
|
||||
"defuse/php-encryption": "^2.2.1",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5.13 || ^8.2.3",
|
||||
"zendframework/zend-diactoros": "^2.1.2",
|
||||
"phpstan/phpstan": "^0.11.8",
|
||||
"phpstan/phpstan-phpunit": "^0.11.2",
|
||||
"roave/security-advisories": "dev-master"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-server.git"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"oauth 2",
|
||||
"oauth 2.0",
|
||||
"server",
|
||||
"auth",
|
||||
"authorization",
|
||||
"authorisation",
|
||||
"authentication",
|
||||
"resource",
|
||||
"api",
|
||||
"auth",
|
||||
"protect",
|
||||
"secure"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andy Millington",
|
||||
"email": "andrew@noexceptions.io",
|
||||
"homepage": "https://www.noexceptions.io",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"replace": {
|
||||
"lncd/oauth2": "*",
|
||||
"league/oauth2server": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"LeagueTests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
53
examples/README.md
Normal file
53
examples/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Example implementations
|
||||
|
||||
## Installation
|
||||
|
||||
0. Run `composer install` in this directory to install dependencies
|
||||
0. Create a private key `openssl genrsa -out private.key 2048`
|
||||
0. Create a public key `openssl rsa -in private.key -pubout > public.key`
|
||||
0. `cd` into the public directory
|
||||
0. Start a PHP server `php -S localhost:4444`
|
||||
|
||||
## Testing the client credentials grant example
|
||||
|
||||
Send the following cURL request:
|
||||
|
||||
```
|
||||
curl -X "POST" "http://localhost:4444/client_credentials.php/access_token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-H "Accept: 1.0" \
|
||||
--data-urlencode "grant_type=client_credentials" \
|
||||
--data-urlencode "client_id=myawesomeapp" \
|
||||
--data-urlencode "client_secret=abc123" \
|
||||
--data-urlencode "scope=basic email"
|
||||
```
|
||||
|
||||
## Testing the password grant example
|
||||
|
||||
Send the following cURL request:
|
||||
|
||||
```
|
||||
curl -X "POST" "http://localhost:4444/password.php/access_token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-H "Accept: 1.0" \
|
||||
--data-urlencode "grant_type=password" \
|
||||
--data-urlencode "client_id=myawesomeapp" \
|
||||
--data-urlencode "client_secret=abc123" \
|
||||
--data-urlencode "username=alex" \
|
||||
--data-urlencode "password=whisky" \
|
||||
--data-urlencode "scope=basic email"
|
||||
```
|
||||
|
||||
## Testing the refresh token grant example
|
||||
|
||||
Send the following cURL request. Replace `{{REFRESH_TOKEN}}` with a refresh token from another grant above:
|
||||
|
||||
```
|
||||
curl -X "POST" "http://localhost:4444/refresh_token.php/access_token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-H "Accept: 1.0" \
|
||||
--data-urlencode "grant_type=refresh_token" \
|
||||
--data-urlencode "client_id=myawesomeapp" \
|
||||
--data-urlencode "client_secret=abc123" \
|
||||
--data-urlencode "refresh_token={{REFRESH_TOKEN}}"
|
||||
```
|
18
examples/composer.json
Normal file
18
examples/composer.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"require": {
|
||||
"slim/slim": "^3.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"league/event": "^2.2",
|
||||
"lcobucci/jwt": "^3.3",
|
||||
"psr/http-message": "^1.0",
|
||||
"defuse/php-encryption": "^2.2",
|
||||
"zendframework/zend-diactoros": "^2.1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OAuth2ServerExamples\\": "src/",
|
||||
"League\\OAuth2\\Server\\": "../src/"
|
||||
}
|
||||
}
|
||||
}
|
647
examples/composer.lock
generated
Normal file
647
examples/composer.lock
generated
Normal file
@@ -0,0 +1,647 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a7f5c3fdcadb17399bbd97f15e1b11f1",
|
||||
"packages": [
|
||||
{
|
||||
"name": "container-interop/container-interop",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/container-interop/container-interop.git",
|
||||
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
|
||||
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Interop\\Container\\": "src/Interop/Container/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
|
||||
"homepage": "https://github.com/container-interop/container-interop",
|
||||
"time": "2017-02-14T19:40:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/fast-route",
|
||||
"version": "v1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/FastRoute.git",
|
||||
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
|
||||
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35|~5.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FastRoute\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nikita Popov",
|
||||
"email": "nikic@php.net"
|
||||
}
|
||||
],
|
||||
"description": "Fast request router for PHP",
|
||||
"keywords": [
|
||||
"router",
|
||||
"routing"
|
||||
],
|
||||
"time": "2018-02-13T20:26:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pimple/pimple",
|
||||
"version": "v3.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/silexphp/Pimple.git",
|
||||
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
|
||||
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^3.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Pimple": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Pimple, a simple Dependency Injection Container",
|
||||
"homepage": "http://pimple.sensiolabs.org",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency injection"
|
||||
],
|
||||
"time": "2018-01-21T07:42:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Container\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common Container Interface (PHP FIG PSR-11)",
|
||||
"homepage": "https://github.com/php-fig/container",
|
||||
"keywords": [
|
||||
"PSR-11",
|
||||
"container",
|
||||
"container-interface",
|
||||
"container-interop",
|
||||
"psr"
|
||||
],
|
||||
"time": "2017-02-14T16:28:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "slim/slim",
|
||||
"version": "3.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/slimphp/Slim.git",
|
||||
"reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/eaee12ef8d0750db62b8c548016d82fb33addb6b",
|
||||
"reference": "eaee12ef8d0750db62b8c548016d82fb33addb6b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"container-interop/container-interop": "^1.2",
|
||||
"nikic/fast-route": "^1.0",
|
||||
"php": ">=5.5.0",
|
||||
"pimple/pimple": "^3.0",
|
||||
"psr/container": "^1.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.0",
|
||||
"squizlabs/php_codesniffer": "^2.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Slim\\": "Slim"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Rob Allen",
|
||||
"email": "rob@akrabat.com",
|
||||
"homepage": "http://akrabat.com"
|
||||
},
|
||||
{
|
||||
"name": "Josh Lockhart",
|
||||
"email": "hello@joshlockhart.com",
|
||||
"homepage": "https://joshlockhart.com"
|
||||
},
|
||||
{
|
||||
"name": "Gabriel Manricks",
|
||||
"email": "gmanricks@me.com",
|
||||
"homepage": "http://gabrielmanricks.com"
|
||||
},
|
||||
{
|
||||
"name": "Andrew Smith",
|
||||
"email": "a.smith@silentworks.co.uk",
|
||||
"homepage": "http://silentworks.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
|
||||
"homepage": "https://slimframework.com",
|
||||
"keywords": [
|
||||
"api",
|
||||
"framework",
|
||||
"micro",
|
||||
"router"
|
||||
],
|
||||
"time": "2019-04-16T16:47:29+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "defuse/php-encryption",
|
||||
"version": "v2.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/defuse/php-encryption.git",
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"paragonie/random_compat": ">= 2",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^2.0|^3.0|^4.0",
|
||||
"phpunit/phpunit": "^4|^5"
|
||||
},
|
||||
"bin": [
|
||||
"bin/generate-defuse-key"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Defuse\\Crypto\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Hornby",
|
||||
"email": "taylor@defuse.ca",
|
||||
"homepage": "https://defuse.ca/"
|
||||
},
|
||||
{
|
||||
"name": "Scott Arciszewski",
|
||||
"email": "info@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "Secure PHP Encryption Library",
|
||||
"keywords": [
|
||||
"aes",
|
||||
"authenticated encryption",
|
||||
"cipher",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encrypt",
|
||||
"encryption",
|
||||
"openssl",
|
||||
"security",
|
||||
"symmetric key cryptography"
|
||||
],
|
||||
"time": "2018-07-24T23:27:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/jwt",
|
||||
"version": "3.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lcobucci/jwt.git",
|
||||
"reference": "a11ec5f4b4d75d1fcd04e133dede4c317aac9e18"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/a11ec5f4b4d75d1fcd04e133dede4c317aac9e18",
|
||||
"reference": "a11ec5f4b4d75d1fcd04e133dede4c317aac9e18",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*",
|
||||
"php": "^5.6 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "~1.5",
|
||||
"phpmd/phpmd": "~2.2",
|
||||
"phpunit/php-invoker": "~1.1",
|
||||
"phpunit/phpunit": "^5.7 || ^7.3",
|
||||
"squizlabs/php_codesniffer": "~2.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lcobucci\\JWT\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Luís Otávio Cobucci Oblonczyk",
|
||||
"email": "lcobucci@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
|
||||
"keywords": [
|
||||
"JWS",
|
||||
"jwt"
|
||||
],
|
||||
"time": "2019-05-24T18:30:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/event",
|
||||
"version": "2.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/event.git",
|
||||
"reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/event/zipball/d2cc124cf9a3fab2bb4ff963307f60361ce4d119",
|
||||
"reference": "d2cc124cf9a3fab2bb4ff963307f60361ce4d119",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"henrikbjorn/phpspec-code-coverage": "~1.0.1",
|
||||
"phpspec/phpspec": "^2.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Event\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frank de Jonge",
|
||||
"email": "info@frenky.net"
|
||||
}
|
||||
],
|
||||
"description": "Event package",
|
||||
"keywords": [
|
||||
"emitter",
|
||||
"event",
|
||||
"listener"
|
||||
],
|
||||
"time": "2018-11-26T11:52:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v9.99.99",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
|
||||
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*",
|
||||
"vimeo/psalm": "^1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"polyfill",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"time": "2018-07-02T15:55:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
|
||||
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2019-04-30T12:38:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "zendframework/zend-diactoros",
|
||||
"version": "2.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zendframework/zend-diactoros.git",
|
||||
"reference": "279723778c40164bcf984a2df12ff2c6ec5e61c1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/279723778c40164bcf984a2df12ff2c6ec5e61c1",
|
||||
"reference": "279723778c40164bcf984a2df12ff2c6ec5e61c1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-factory-implementation": "1.0",
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"http-interop/http-factory-tests": "^0.5.0",
|
||||
"php-http/psr7-integration-tests": "dev-master",
|
||||
"phpunit/phpunit": "^7.0.2",
|
||||
"zendframework/zend-coding-standard": "~1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1.x-dev",
|
||||
"dev-develop": "2.2.x-dev",
|
||||
"dev-release-1.8": "1.8.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions/create_uploaded_file.php",
|
||||
"src/functions/marshal_headers_from_sapi.php",
|
||||
"src/functions/marshal_method_from_sapi.php",
|
||||
"src/functions/marshal_protocol_version_from_sapi.php",
|
||||
"src/functions/marshal_uri_from_sapi.php",
|
||||
"src/functions/normalize_server.php",
|
||||
"src/functions/normalize_uploaded_files.php",
|
||||
"src/functions/parse_cookie_header.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Zend\\Diactoros\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "PSR HTTP Message implementations",
|
||||
"keywords": [
|
||||
"http",
|
||||
"psr",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2019-07-10T16:13:25+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
74
examples/public/api.php
Normal file
74
examples/public/api.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
use League\OAuth2\Server\ResourceServer;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
// Add the resource server to the DI container
|
||||
ResourceServer::class => function () {
|
||||
$server = new ResourceServer(
|
||||
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
|
||||
'file://' . __DIR__ . '/../public.key' // the authorization server's public key
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
// Add the resource server middleware which will intercept and validate requests
|
||||
$app->add(
|
||||
new \League\OAuth2\Server\Middleware\ResourceServerMiddleware(
|
||||
$app->getContainer()->get(ResourceServer::class)
|
||||
)
|
||||
);
|
||||
|
||||
// An example endpoint secured with OAuth 2.0
|
||||
$app->get(
|
||||
'/users',
|
||||
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
$users = [
|
||||
[
|
||||
'id' => 123,
|
||||
'name' => 'Alex',
|
||||
'email' => 'alex@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 124,
|
||||
'name' => 'Frank',
|
||||
'email' => 'frank@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 125,
|
||||
'name' => 'Phil',
|
||||
'email' => 'phil@thephpleague.com',
|
||||
],
|
||||
];
|
||||
|
||||
$totalUsers = count($users);
|
||||
|
||||
// If the access token doesn't have the `basic` scope hide users' names
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < $totalUsers; $i++) {
|
||||
unset($users[$i]['name']);
|
||||
}
|
||||
}
|
||||
|
||||
// If the access token doesn't have the `email` scope hide users' email addresses
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < $totalUsers; $i++) {
|
||||
unset($users[$i]['email']);
|
||||
}
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($users));
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
);
|
||||
|
||||
$app->run();
|
107
examples/public/auth_code.php
Normal file
107
examples/public/auth_code.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Grant\AuthCodeGrant;
|
||||
use OAuth2ServerExamples\Entities\UserEntity;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$authCodeRepository = new AuthCodeRepository();
|
||||
$refreshTokenRepository = new RefreshTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the authentication code grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(
|
||||
new AuthCodeGrant(
|
||||
$authCodeRepository,
|
||||
$refreshTokenRepository,
|
||||
new \DateInterval('PT10M')
|
||||
),
|
||||
new \DateInterval('PT1H')
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
// Validate the HTTP request and return an AuthorizationRequest object.
|
||||
// The auth request object can be serialized into a user's session
|
||||
$authRequest = $server->validateAuthorizationRequest($request);
|
||||
|
||||
// Once the user has logged in set the user on the AuthorizationRequest
|
||||
$authRequest->setUser(new UserEntity());
|
||||
|
||||
// Once the user has approved or denied the client update the status
|
||||
// (true = approved, false = denied)
|
||||
$authRequest->setAuthorizationApproved(true);
|
||||
|
||||
// Return the HTTP redirect response
|
||||
return $server->completeAuthorizationRequest($authRequest, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
78
examples/public/client_credentials.php
Normal file
78
examples/public/client_credentials.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface
|
||||
$scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface
|
||||
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
|
||||
|
||||
// Path to public and private keys
|
||||
$privateKey = 'file://' . __DIR__ . '/../private.key';
|
||||
//$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKey,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the client credentials grant on the server
|
||||
$server->enableGrantType(
|
||||
new \League\OAuth2\Server\Grant\ClientCredentialsGrant(),
|
||||
new \DateInterval('PT1H') // access tokens will expire after 1 hour
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
|
||||
// Try to respond to the request
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
|
||||
// All instances of OAuthServerException can be formatted into a HTTP response
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
|
||||
// Unknown exception
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
80
examples/public/implicit.php
Normal file
80
examples/public/implicit.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Grant\ImplicitGrant;
|
||||
use OAuth2ServerExamples\Entities\UserEntity;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the implicit grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H')));
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
// Validate the HTTP request and return an AuthorizationRequest object.
|
||||
// The auth request object can be serialized into a user's session
|
||||
$authRequest = $server->validateAuthorizationRequest($request);
|
||||
|
||||
// Once the user has logged in set the user on the AuthorizationRequest
|
||||
$authRequest->setUser(new UserEntity());
|
||||
|
||||
// Once the user has approved or denied the client update the status
|
||||
// (true = approved, false = denied)
|
||||
$authRequest->setAuthorizationApproved(true);
|
||||
|
||||
// Return the HTTP redirect response
|
||||
return $server->completeAuthorizationRequest($authRequest, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
109
examples/public/middleware_use.php
Normal file
109
examples/public/middleware_use.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Grant\AuthCodeGrant;
|
||||
use League\OAuth2\Server\Grant\RefreshTokenGrant;
|
||||
use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware;
|
||||
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
|
||||
use League\OAuth2\Server\ResourceServer;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$authCodeRepository = new AuthCodeRepository();
|
||||
$refreshTokenRepository = new RefreshTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the authentication code grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(
|
||||
new AuthCodeGrant(
|
||||
$authCodeRepository,
|
||||
$refreshTokenRepository,
|
||||
new \DateInterval('PT10M')
|
||||
),
|
||||
new \DateInterval('PT1H')
|
||||
);
|
||||
|
||||
// Enable the refresh token grant on the server with a token TTL of 1 month
|
||||
$server->enableGrantType(
|
||||
new RefreshTokenGrant($refreshTokenRepository),
|
||||
new \DateInterval('P1M')
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
ResourceServer::class => function () {
|
||||
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
|
||||
|
||||
$server = new ResourceServer(
|
||||
new AccessTokenRepository(),
|
||||
$publicKeyPath
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
// Access token issuer
|
||||
$app->post('/access_token', function () {
|
||||
})->add(new AuthorizationServerMiddleware($app->getContainer()->get(AuthorizationServer::class)));
|
||||
|
||||
// Secured API
|
||||
$app->group('/api', function () {
|
||||
$this->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) {
|
||||
$params = [];
|
||||
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes', []))) {
|
||||
$params = [
|
||||
'id' => 1,
|
||||
'name' => 'Alex',
|
||||
'city' => 'London',
|
||||
];
|
||||
}
|
||||
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes', []))) {
|
||||
$params['email'] = 'alex@example.com';
|
||||
}
|
||||
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write(json_encode($params));
|
||||
|
||||
return $response->withBody($body);
|
||||
});
|
||||
})->add(new ResourceServerMiddleware($app->getContainer()->get(ResourceServer::class)));
|
||||
|
||||
$app->run();
|
72
examples/public/password.php
Normal file
72
examples/public/password.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Grant\PasswordGrant;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use OAuth2ServerExamples\Repositories\UserRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
// Add the authorization server to the DI container
|
||||
AuthorizationServer::class => function () {
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
new ClientRepository(), // instance of ClientRepositoryInterface
|
||||
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
|
||||
new ScopeRepository(), // instance of ScopeRepositoryInterface
|
||||
'file://' . __DIR__ . '/../private.key', // path to private key
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' // encryption key
|
||||
);
|
||||
|
||||
$grant = new PasswordGrant(
|
||||
new UserRepository(), // instance of UserRepositoryInterface
|
||||
new RefreshTokenRepository() // instance of RefreshTokenRepositoryInterface
|
||||
);
|
||||
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month
|
||||
|
||||
// Enable the password grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(
|
||||
$grant,
|
||||
new \DateInterval('PT1H') // access tokens will expire after 1 hour
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->post(
|
||||
'/access_token',
|
||||
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
|
||||
// Try to respond to the access token request
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
|
||||
// All instances of OAuthServerException can be converted to a PSR-7 response
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
|
||||
// Catch unexpected exceptions
|
||||
$body = $response->getBody();
|
||||
$body->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$app->run();
|
73
examples/public/refresh_token.php
Normal file
73
examples/public/refresh_token.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Grant\RefreshTokenGrant;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$refreshTokenRepository = new RefreshTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||
);
|
||||
|
||||
// Enable the refresh token grant on the server
|
||||
$grant = new RefreshTokenGrant($refreshTokenRepository);
|
||||
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // The refresh token will expire in 1 month
|
||||
|
||||
$server->enableGrantType(
|
||||
$grant,
|
||||
new \DateInterval('PT1H') // The new access token will expire after 1 hour
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
try {
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$response->getBody()->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
$app->run();
|
20
examples/src/Entities/AccessTokenEntity.php
Normal file
20
examples/src/Entities/AccessTokenEntity.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\AccessTokenTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||
|
||||
class AccessTokenEntity implements AccessTokenEntityInterface
|
||||
{
|
||||
use AccessTokenTrait, TokenEntityTrait, EntityTrait;
|
||||
}
|
20
examples/src/Entities/AuthCodeEntity.php
Normal file
20
examples/src/Entities/AuthCodeEntity.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\AuthCodeTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||
|
||||
class AuthCodeEntity implements AuthCodeEntityInterface
|
||||
{
|
||||
use EntityTrait, TokenEntityTrait, AuthCodeTrait;
|
||||
}
|
29
examples/src/Entities/ClientEntity.php
Normal file
29
examples/src/Entities/ClientEntity.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\ClientTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
|
||||
class ClientEntity implements ClientEntityInterface
|
||||
{
|
||||
use EntityTrait, ClientTrait;
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function setRedirectUri($uri)
|
||||
{
|
||||
$this->redirectUri = $uri;
|
||||
}
|
||||
}
|
19
examples/src/Entities/RefreshTokenEntity.php
Normal file
19
examples/src/Entities/RefreshTokenEntity.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
|
||||
|
||||
class RefreshTokenEntity implements RefreshTokenEntityInterface
|
||||
{
|
||||
use RefreshTokenTrait, EntityTrait;
|
||||
}
|
19
examples/src/Entities/ScopeEntity.php
Normal file
19
examples/src/Entities/ScopeEntity.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\ScopeTrait;
|
||||
|
||||
class ScopeEntity implements ScopeEntityInterface
|
||||
{
|
||||
use EntityTrait, ScopeTrait;
|
||||
}
|
25
examples/src/Entities/UserEntity.php
Normal file
25
examples/src/Entities/UserEntity.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
|
||||
class UserEntity implements UserEntityInterface
|
||||
{
|
||||
/**
|
||||
* Return the user's identifier.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
57
examples/src/Repositories/AccessTokenRepository.php
Normal file
57
examples/src/Repositories/AccessTokenRepository.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\AccessTokenEntity;
|
||||
|
||||
class AccessTokenRepository implements AccessTokenRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity)
|
||||
{
|
||||
// Some logic here to save the access token to a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revokeAccessToken($tokenId)
|
||||
{
|
||||
// Some logic here to revoke the access token
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isAccessTokenRevoked($tokenId)
|
||||
{
|
||||
return false; // Access token hasn't been revoked
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null)
|
||||
{
|
||||
$accessToken = new AccessTokenEntity();
|
||||
$accessToken->setClient($clientEntity);
|
||||
foreach ($scopes as $scope) {
|
||||
$accessToken->addScope($scope);
|
||||
}
|
||||
$accessToken->setUserIdentifier($userIdentifier);
|
||||
|
||||
return $accessToken;
|
||||
}
|
||||
}
|
49
examples/src/Repositories/AuthCodeRepository.php
Normal file
49
examples/src/Repositories/AuthCodeRepository.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\AuthCodeEntity;
|
||||
|
||||
class AuthCodeRepository implements AuthCodeRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity)
|
||||
{
|
||||
// Some logic to persist the auth code to a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revokeAuthCode($codeId)
|
||||
{
|
||||
// Some logic to revoke the auth code in a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isAuthCodeRevoked($codeId)
|
||||
{
|
||||
return false; // The auth code has not been revoked
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNewAuthCode()
|
||||
{
|
||||
return new AuthCodeEntity();
|
||||
}
|
||||
}
|
60
examples/src/Repositories/ClientRepository.php
Normal file
60
examples/src/Repositories/ClientRepository.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\ClientEntity;
|
||||
|
||||
class ClientRepository implements ClientRepositoryInterface
|
||||
{
|
||||
const CLIENT_NAME = 'My Awesome App';
|
||||
const REDIRECT_URI = 'http://foo/bar';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClientEntity($clientIdentifier)
|
||||
{
|
||||
$client = new ClientEntity();
|
||||
|
||||
$client->setIdentifier($clientIdentifier);
|
||||
$client->setName(self::CLIENT_NAME);
|
||||
$client->setRedirectUri(self::REDIRECT_URI);
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateClient($clientIdentifier, $clientSecret, $grantType)
|
||||
{
|
||||
$clients = [
|
||||
'myawesomeapp' => [
|
||||
'secret' => password_hash('abc123', PASSWORD_BCRYPT),
|
||||
'name' => self::CLIENT_NAME,
|
||||
'redirect_uri' => self::REDIRECT_URI,
|
||||
'is_confidential' => true,
|
||||
],
|
||||
];
|
||||
|
||||
// Check if client is registered
|
||||
if (array_key_exists($clientIdentifier, $clients) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
$clients[$clientIdentifier]['is_confidential'] === true
|
||||
&& password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
49
examples/src/Repositories/RefreshTokenRepository.php
Normal file
49
examples/src/Repositories/RefreshTokenRepository.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\RefreshTokenEntity;
|
||||
|
||||
class RefreshTokenRepository implements RefreshTokenRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity)
|
||||
{
|
||||
// Some logic to persist the refresh token in a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function revokeRefreshToken($tokenId)
|
||||
{
|
||||
// Some logic to revoke the refresh token in a database
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRefreshTokenRevoked($tokenId)
|
||||
{
|
||||
return false; // The refresh token has not been revoked
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNewRefreshToken()
|
||||
{
|
||||
return new RefreshTokenEntity();
|
||||
}
|
||||
}
|
60
examples/src/Repositories/ScopeRepository.php
Normal file
60
examples/src/Repositories/ScopeRepository.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\ScopeEntity;
|
||||
|
||||
class ScopeRepository implements ScopeRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getScopeEntityByIdentifier($scopeIdentifier)
|
||||
{
|
||||
$scopes = [
|
||||
'basic' => [
|
||||
'description' => 'Basic details about you',
|
||||
],
|
||||
'email' => [
|
||||
'description' => 'Your email address',
|
||||
],
|
||||
];
|
||||
|
||||
if (array_key_exists($scopeIdentifier, $scopes) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scope = new ScopeEntity();
|
||||
$scope->setIdentifier($scopeIdentifier);
|
||||
|
||||
return $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finalizeScopes(
|
||||
array $scopes,
|
||||
$grantType,
|
||||
ClientEntityInterface $clientEntity,
|
||||
$userIdentifier = null
|
||||
) {
|
||||
// Example of programatically modifying the final scope of the access token
|
||||
if ((int) $userIdentifier === 1) {
|
||||
$scope = new ScopeEntity();
|
||||
$scope->setIdentifier('email');
|
||||
$scopes[] = $scope;
|
||||
}
|
||||
|
||||
return $scopes;
|
||||
}
|
||||
}
|
33
examples/src/Repositories/UserRepository.php
Normal file
33
examples/src/Repositories/UserRepository.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace OAuth2ServerExamples\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||
use OAuth2ServerExamples\Entities\UserEntity;
|
||||
|
||||
class UserRepository implements UserRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUserEntityByUserCredentials(
|
||||
$username,
|
||||
$password,
|
||||
$grantType,
|
||||
ClientEntityInterface $clientEntity
|
||||
) {
|
||||
if ($username === 'alex' && $password === 'whisky') {
|
||||
return new UserEntity();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
8
phpstan.neon
Normal file
8
phpstan.neon
Normal file
@@ -0,0 +1,8 @@
|
||||
includes:
|
||||
- vendor/phpstan/phpstan-phpunit/extension.neon
|
||||
- vendor/phpstan/phpstan-phpunit/rules.neon
|
||||
services:
|
||||
-
|
||||
class: LeagueTests\PHPStan\AbstractGrantExtension
|
||||
tags:
|
||||
- phpstan.broker.dynamicMethodReturnTypeExtension
|
24
phpunit.xml.dist
Normal file
24
phpunit.xml.dist
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="true"
|
||||
stopOnFailure="true" stopOnIncomplete="false" stopOnSkipped="false" bootstrap="tests/Bootstrap.php">
|
||||
<testsuites>
|
||||
<testsuite name="Tests">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">src/ResponseTypes/DefaultTemplates</directory>
|
||||
<directory suffix=".php">src/TemplateRenderer</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" title="thephpleague/oauth2-server" charset="UTF-8" yui="true"
|
||||
highlight="true" lowUpperBound="60" highLowerBound="90"/>
|
||||
<log type="coverage-html" target="build/coverage" title="thephpleague/oauth2-server" charset="UTF-8" yui="true"
|
||||
highlight="true" lowUpperBound="60" highLowerBound="90"/>
|
||||
</logging>
|
||||
</phpunit>
|
@@ -1,60 +0,0 @@
|
||||
-- Create syntax for TABLE 'clients'
|
||||
CREATE TABLE `clients` (
|
||||
`id` varchar(40) NOT NULL DEFAULT '',
|
||||
`secret` varchar(40) NOT NULL DEFAULT '',
|
||||
`name` varchar(255) NOT NULL DEFAULT '',
|
||||
`auto_approve` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Create syntax for TABLE 'client_endpoints'
|
||||
CREATE TABLE `client_endpoints` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`client_id` varchar(40) NOT NULL DEFAULT '',
|
||||
`redirect_uri` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `client_id` (`client_id`),
|
||||
CONSTRAINT `client_endpoints_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Create syntax for TABLE 'oauth_sessions'
|
||||
CREATE TABLE `oauth_sessions` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`client_id` varchar(40) NOT NULL DEFAULT '',
|
||||
`redirect_uri` varchar(250) DEFAULT '',
|
||||
`owner_type` enum('user','client') NOT NULL DEFAULT 'user',
|
||||
`owner_id` varchar(255) DEFAULT NULL,
|
||||
`auth_code` varchar(40) DEFAULT '',
|
||||
`access_token` varchar(40) DEFAULT '',
|
||||
`refresh_token` varchar(40) NOT NULL,
|
||||
`access_token_expires` int(10) DEFAULT NULL,
|
||||
`stage` enum('requested','granted') NOT NULL DEFAULT 'requested',
|
||||
`first_requested` int(10) unsigned NOT NULL,
|
||||
`last_updated` int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `client_id` (`client_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Create syntax for TABLE 'scopes'
|
||||
CREATE TABLE `scopes` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`scope` varchar(255) NOT NULL DEFAULT '',
|
||||
`name` varchar(255) NOT NULL DEFAULT '',
|
||||
`description` varchar(255) DEFAULT '',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `scope` (`scope`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Create syntax for TABLE 'oauth_session_scopes'
|
||||
CREATE TABLE `oauth_session_scopes` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`session_id` int(11) unsigned NOT NULL,
|
||||
`access_token` varchar(40) NOT NULL DEFAULT '',
|
||||
`scope` varchar(255) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `session_id` (`session_id`),
|
||||
KEY `access_token` (`access_token`),
|
||||
KEY `scope` (`scope`),
|
||||
CONSTRAINT `oauth_session_scopes_ibfk_3` FOREIGN KEY (`scope`) REFERENCES `scopes` (`scope`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `oauth_session_scopes_ibfk_4` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
236
src/AuthorizationServer.php
Normal file
236
src/AuthorizationServer.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
use DateInterval;
|
||||
use Defuse\Crypto\Key;
|
||||
use League\Event\EmitterAwareInterface;
|
||||
use League\Event\EmitterAwareTrait;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Grant\GrantTypeInterface;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\AbstractResponseType;
|
||||
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class AuthorizationServer implements EmitterAwareInterface
|
||||
{
|
||||
use EmitterAwareTrait;
|
||||
|
||||
/**
|
||||
* @var GrantTypeInterface[]
|
||||
*/
|
||||
protected $enabledGrantTypes = [];
|
||||
|
||||
/**
|
||||
* @var DateInterval[]
|
||||
*/
|
||||
protected $grantTypeAccessTokenTTL = [];
|
||||
|
||||
/**
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $publicKey;
|
||||
|
||||
/**
|
||||
* @var ResponseTypeInterface
|
||||
*/
|
||||
protected $responseType;
|
||||
|
||||
/**
|
||||
* @var ClientRepositoryInterface
|
||||
*/
|
||||
private $clientRepository;
|
||||
|
||||
/**
|
||||
* @var AccessTokenRepositoryInterface
|
||||
*/
|
||||
private $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* @var ScopeRepositoryInterface
|
||||
*/
|
||||
private $scopeRepository;
|
||||
|
||||
/**
|
||||
* @var string|Key
|
||||
*/
|
||||
private $encryptionKey;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $defaultScope = '';
|
||||
|
||||
/**
|
||||
* New server instance.
|
||||
*
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
* @param CryptKey|string $privateKey
|
||||
* @param string|Key $encryptionKey
|
||||
* @param null|ResponseTypeInterface $responseType
|
||||
*/
|
||||
public function __construct(
|
||||
ClientRepositoryInterface $clientRepository,
|
||||
AccessTokenRepositoryInterface $accessTokenRepository,
|
||||
ScopeRepositoryInterface $scopeRepository,
|
||||
$privateKey,
|
||||
$encryptionKey,
|
||||
ResponseTypeInterface $responseType = null
|
||||
) {
|
||||
$this->clientRepository = $clientRepository;
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
|
||||
if ($privateKey instanceof CryptKey === false) {
|
||||
$privateKey = new CryptKey($privateKey);
|
||||
}
|
||||
|
||||
$this->privateKey = $privateKey;
|
||||
$this->encryptionKey = $encryptionKey;
|
||||
|
||||
if ($responseType === null) {
|
||||
$responseType = new BearerTokenResponse();
|
||||
} else {
|
||||
$responseType = clone $responseType;
|
||||
}
|
||||
|
||||
$this->responseType = $responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a grant type on the server.
|
||||
*
|
||||
* @param GrantTypeInterface $grantType
|
||||
* @param null|DateInterval $accessTokenTTL
|
||||
*/
|
||||
public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null)
|
||||
{
|
||||
if ($accessTokenTTL === null) {
|
||||
$accessTokenTTL = new DateInterval('PT1H');
|
||||
}
|
||||
|
||||
$grantType->setAccessTokenRepository($this->accessTokenRepository);
|
||||
$grantType->setClientRepository($this->clientRepository);
|
||||
$grantType->setScopeRepository($this->scopeRepository);
|
||||
$grantType->setDefaultScope($this->defaultScope);
|
||||
$grantType->setPrivateKey($this->privateKey);
|
||||
$grantType->setEmitter($this->getEmitter());
|
||||
$grantType->setEncryptionKey($this->encryptionKey);
|
||||
|
||||
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
|
||||
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an authorization request
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return AuthorizationRequest
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
foreach ($this->enabledGrantTypes as $grantType) {
|
||||
if ($grantType->canRespondToAuthorizationRequest($request)) {
|
||||
return $grantType->validateAuthorizationRequest($request);
|
||||
}
|
||||
}
|
||||
|
||||
throw OAuthServerException::unsupportedGrantType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete an authorization request
|
||||
*
|
||||
* @param AuthorizationRequest $authRequest
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
|
||||
{
|
||||
return $this->enabledGrantTypes[$authRequest->getGrantTypeId()]
|
||||
->completeAuthorizationRequest($authRequest)
|
||||
->generateHttpResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an access token response.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
foreach ($this->enabledGrantTypes as $grantType) {
|
||||
if (!$grantType->canRespondToAccessTokenRequest($request)) {
|
||||
continue;
|
||||
}
|
||||
$tokenResponse = $grantType->respondToAccessTokenRequest(
|
||||
$request,
|
||||
$this->getResponseType(),
|
||||
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
|
||||
);
|
||||
|
||||
if ($tokenResponse instanceof ResponseTypeInterface) {
|
||||
return $tokenResponse->generateHttpResponse($response);
|
||||
}
|
||||
}
|
||||
|
||||
throw OAuthServerException::unsupportedGrantType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token type that grants will return in the HTTP response.
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
protected function getResponseType()
|
||||
{
|
||||
$responseType = clone $this->responseType;
|
||||
|
||||
if ($responseType instanceof AbstractResponseType) {
|
||||
$responseType->setPrivateKey($this->privateKey);
|
||||
}
|
||||
|
||||
$responseType->setEncryptionKey($this->encryptionKey);
|
||||
|
||||
return $responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default scope for the authorization server.
|
||||
*
|
||||
* @param string $defaultScope
|
||||
*/
|
||||
public function setDefaultScope($defaultScope)
|
||||
{
|
||||
$this->defaultScope = $defaultScope;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\AuthorizationValidators;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
interface AuthorizationValidatorInterface
|
||||
{
|
||||
/**
|
||||
* Determine the access token in the authorization header and append OAUth properties to the request
|
||||
* as attributes.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
public function validateAuthorization(ServerRequestInterface $request);
|
||||
}
|
106
src/AuthorizationValidators/BearerTokenValidator.php
Normal file
106
src/AuthorizationValidators/BearerTokenValidator.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\AuthorizationValidators;
|
||||
|
||||
use BadMethodCallException;
|
||||
use InvalidArgumentException;
|
||||
use Lcobucci\JWT\Parser;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Lcobucci\JWT\ValidationData;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\CryptTrait;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
{
|
||||
use CryptTrait;
|
||||
|
||||
/**
|
||||
* @var AccessTokenRepositoryInterface
|
||||
*/
|
||||
private $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $publicKey;
|
||||
|
||||
/**
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
|
||||
{
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the public key
|
||||
*
|
||||
* @param CryptKey $key
|
||||
*/
|
||||
public function setPublicKey(CryptKey $key)
|
||||
{
|
||||
$this->publicKey = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthorization(ServerRequestInterface $request)
|
||||
{
|
||||
if ($request->hasHeader('authorization') === false) {
|
||||
throw OAuthServerException::accessDenied('Missing "Authorization" header');
|
||||
}
|
||||
|
||||
$header = $request->getHeader('authorization');
|
||||
$jwt = trim((string) preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
|
||||
|
||||
try {
|
||||
// Attempt to parse and validate the JWT
|
||||
$token = (new Parser())->parse($jwt);
|
||||
try {
|
||||
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
|
||||
throw OAuthServerException::accessDenied('Access token could not be verified');
|
||||
}
|
||||
} catch (BadMethodCallException $exception) {
|
||||
throw OAuthServerException::accessDenied('Access token is not signed', null, $exception);
|
||||
}
|
||||
|
||||
// Ensure access token hasn't expired
|
||||
$data = new ValidationData();
|
||||
$data->setCurrentTime(time());
|
||||
|
||||
if ($token->validate($data) === false) {
|
||||
throw OAuthServerException::accessDenied('Access token is invalid');
|
||||
}
|
||||
|
||||
// Check if token has been revoked
|
||||
if ($this->accessTokenRepository->isAccessTokenRevoked($token->getClaim('jti'))) {
|
||||
throw OAuthServerException::accessDenied('Access token has been revoked');
|
||||
}
|
||||
|
||||
// Return the request with additional attributes
|
||||
return $request
|
||||
->withAttribute('oauth_access_token_id', $token->getClaim('jti'))
|
||||
->withAttribute('oauth_client_id', $token->getClaim('aud'))
|
||||
->withAttribute('oauth_user_id', $token->getClaim('sub'))
|
||||
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
|
||||
} catch (InvalidArgumentException $exception) {
|
||||
// JWT couldn't be parsed so return the request as is
|
||||
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
|
||||
} catch (RuntimeException $exception) {
|
||||
//JWR couldn't be parsed so return the request as is
|
||||
throw OAuthServerException::accessDenied('Error while decoding to JSON', null, $exception);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Lukáš Unger <lookymsc@gmail.com>
|
||||
* @copyright Copyright (c) Lukáš Unger
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\CodeChallengeVerifiers;
|
||||
|
||||
interface CodeChallengeVerifierInterface
|
||||
{
|
||||
/**
|
||||
* Return code challenge method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod();
|
||||
|
||||
/**
|
||||
* Verify the code challenge.
|
||||
*
|
||||
* @param string $codeVerifier
|
||||
* @param string $codeChallenge
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyCodeChallenge($codeVerifier, $codeChallenge);
|
||||
}
|
36
src/CodeChallengeVerifiers/PlainVerifier.php
Normal file
36
src/CodeChallengeVerifiers/PlainVerifier.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Lukáš Unger <lookymsc@gmail.com>
|
||||
* @copyright Copyright (c) Lukáš Unger
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\CodeChallengeVerifiers;
|
||||
|
||||
class PlainVerifier implements CodeChallengeVerifierInterface
|
||||
{
|
||||
/**
|
||||
* Return code challenge method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'plain';
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the code challenge.
|
||||
*
|
||||
* @param string $codeVerifier
|
||||
* @param string $codeChallenge
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyCodeChallenge($codeVerifier, $codeChallenge)
|
||||
{
|
||||
return hash_equals($codeVerifier, $codeChallenge);
|
||||
}
|
||||
}
|
39
src/CodeChallengeVerifiers/S256Verifier.php
Normal file
39
src/CodeChallengeVerifiers/S256Verifier.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Lukáš Unger <lookymsc@gmail.com>
|
||||
* @copyright Copyright (c) Lukáš Unger
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\CodeChallengeVerifiers;
|
||||
|
||||
class S256Verifier implements CodeChallengeVerifierInterface
|
||||
{
|
||||
/**
|
||||
* Return code challenge method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return 'S256';
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the code challenge.
|
||||
*
|
||||
* @param string $codeVerifier
|
||||
* @param string $codeChallenge
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyCodeChallenge($codeVerifier, $codeChallenge)
|
||||
{
|
||||
return hash_equals(
|
||||
strtr(rtrim(base64_encode(hash('sha256', $codeVerifier, true)), '='), '+/', '-_'),
|
||||
$codeChallenge
|
||||
);
|
||||
}
|
||||
}
|
123
src/CryptKey.php
Normal file
123
src/CryptKey.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* Cryptography key holder.
|
||||
*
|
||||
* @author Julián Gutiérrez <juliangut@gmail.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
|
||||
class CryptKey
|
||||
{
|
||||
const RSA_KEY_PATTERN =
|
||||
'/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)\R.*(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\R?$/s';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $keyPath;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
protected $passPhrase;
|
||||
|
||||
/**
|
||||
* @param string $keyPath
|
||||
* @param null|string $passPhrase
|
||||
* @param bool $keyPermissionsCheck
|
||||
*/
|
||||
public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true)
|
||||
{
|
||||
if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) {
|
||||
$keyPath = $this->saveKeyToFile($keyPath);
|
||||
}
|
||||
|
||||
if (strpos($keyPath, 'file://') !== 0) {
|
||||
$keyPath = 'file://' . $keyPath;
|
||||
}
|
||||
|
||||
if (!file_exists($keyPath) || !is_readable($keyPath)) {
|
||||
throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
|
||||
}
|
||||
|
||||
if ($keyPermissionsCheck === true) {
|
||||
// Verify the permissions of the key
|
||||
$keyPathPerms = decoct(fileperms($keyPath) & 0777);
|
||||
if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
|
||||
trigger_error(sprintf(
|
||||
'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
|
||||
$keyPath,
|
||||
$keyPathPerms
|
||||
), E_USER_NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
$this->keyPath = $keyPath;
|
||||
$this->passPhrase = $passPhrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function saveKeyToFile($key)
|
||||
{
|
||||
$tmpDir = sys_get_temp_dir();
|
||||
$keyPath = $tmpDir . '/' . sha1($key) . '.key';
|
||||
|
||||
if (file_exists($keyPath)) {
|
||||
return 'file://' . $keyPath;
|
||||
}
|
||||
|
||||
if (!touch($keyPath)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new RuntimeException(sprintf('"%s" key file could not be created', $keyPath));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (file_put_contents($keyPath, $key) === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new RuntimeException(sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if (chmod($keyPath, 0600) === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new RuntimeException(sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
return 'file://' . $keyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve key path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getKeyPath()
|
||||
{
|
||||
return $this->keyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve key pass phrase.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getPassPhrase()
|
||||
{
|
||||
return $this->passPhrase;
|
||||
}
|
||||
}
|
87
src/CryptTrait.php
Normal file
87
src/CryptTrait.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* Encrypt/decrypt with encryptionKey.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
use Defuse\Crypto\Crypto;
|
||||
use Defuse\Crypto\Key;
|
||||
use Exception;
|
||||
use LogicException;
|
||||
|
||||
trait CryptTrait
|
||||
{
|
||||
/**
|
||||
* @var string|Key|null
|
||||
*/
|
||||
protected $encryptionKey;
|
||||
|
||||
/**
|
||||
* Encrypt data with encryptionKey.
|
||||
*
|
||||
* @param string $unencryptedData
|
||||
*
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function encrypt($unencryptedData)
|
||||
{
|
||||
try {
|
||||
if ($this->encryptionKey instanceof Key) {
|
||||
return Crypto::encrypt($unencryptedData, $this->encryptionKey);
|
||||
}
|
||||
|
||||
if (is_string($this->encryptionKey)) {
|
||||
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
|
||||
}
|
||||
|
||||
throw new LogicException('Encryption key not set when attempting to encrypt');
|
||||
} catch (Exception $e) {
|
||||
throw new LogicException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data with encryptionKey.
|
||||
*
|
||||
* @param string $encryptedData
|
||||
*
|
||||
* @throws LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function decrypt($encryptedData)
|
||||
{
|
||||
try {
|
||||
if ($this->encryptionKey instanceof Key) {
|
||||
return Crypto::decrypt($encryptedData, $this->encryptionKey);
|
||||
}
|
||||
|
||||
if (is_string($this->encryptionKey)) {
|
||||
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
|
||||
}
|
||||
|
||||
throw new LogicException('Encryption key not set when attempting to decrypt');
|
||||
} catch (Exception $e) {
|
||||
throw new LogicException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encryption key
|
||||
*
|
||||
* @param string|Key $key
|
||||
*/
|
||||
public function setEncryptionKey($key = null)
|
||||
{
|
||||
$this->encryptionKey = $key;
|
||||
}
|
||||
}
|
25
src/Entities/AccessTokenEntityInterface.php
Normal file
25
src/Entities/AccessTokenEntityInterface.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
|
||||
interface AccessTokenEntityInterface extends TokenInterface
|
||||
{
|
||||
/**
|
||||
* Set a private key used to encrypt the access token.
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $privateKey);
|
||||
|
||||
/**
|
||||
* Generate a string representation of the access token.
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
23
src/Entities/AuthCodeEntityInterface.php
Normal file
23
src/Entities/AuthCodeEntityInterface.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
interface AuthCodeEntityInterface extends TokenInterface
|
||||
{
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRedirectUri();
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
*/
|
||||
public function setRedirectUri($uri);
|
||||
}
|
43
src/Entities/ClientEntityInterface.php
Normal file
43
src/Entities/ClientEntityInterface.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
interface ClientEntityInterface
|
||||
{
|
||||
/**
|
||||
* Get the client's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Get the client's name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Returns the registered redirect URI (as a string).
|
||||
*
|
||||
* Alternatively return an indexed array of redirect URIs.
|
||||
*
|
||||
* @return string|string[]
|
||||
*/
|
||||
public function getRedirectUri();
|
||||
|
||||
/**
|
||||
* Returns true if the client is confidential.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConfidential();
|
||||
}
|
57
src/Entities/RefreshTokenEntityInterface.php
Normal file
57
src/Entities/RefreshTokenEntityInterface.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
interface RefreshTokenEntityInterface
|
||||
{
|
||||
/**
|
||||
* Get the token's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Set the token's identifier.
|
||||
*
|
||||
* @param mixed $identifier
|
||||
*/
|
||||
public function setIdentifier($identifier);
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return DateTimeImmutable
|
||||
*/
|
||||
public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param DateTimeImmutable $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(DateTimeImmutable $dateTime);
|
||||
|
||||
/**
|
||||
* Set the access token that the refresh token was associated with.
|
||||
*
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*/
|
||||
public function setAccessToken(AccessTokenEntityInterface $accessToken);
|
||||
|
||||
/**
|
||||
* Get the access token that the refresh token was originally associated with.
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
public function getAccessToken();
|
||||
}
|
22
src/Entities/ScopeEntityInterface.php
Normal file
22
src/Entities/ScopeEntityInterface.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
interface ScopeEntityInterface extends JsonSerializable
|
||||
{
|
||||
/**
|
||||
* Get the scope's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
}
|
85
src/Entities/TokenInterface.php
Normal file
85
src/Entities/TokenInterface.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
interface TokenInterface
|
||||
{
|
||||
/**
|
||||
* Get the token's identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Set the token's identifier.
|
||||
*
|
||||
* @param mixed $identifier
|
||||
*/
|
||||
public function setIdentifier($identifier);
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return DateTimeImmutable
|
||||
*/
|
||||
public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param DateTimeImmutable $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(DateTimeImmutable $dateTime);
|
||||
|
||||
/**
|
||||
* Set the identifier of the user associated with the token.
|
||||
*
|
||||
* @param string|int|null $identifier The identifier of the user
|
||||
*/
|
||||
public function setUserIdentifier($identifier);
|
||||
|
||||
/**
|
||||
* Get the token user's identifier.
|
||||
*
|
||||
* @return string|int|null
|
||||
*/
|
||||
public function getUserIdentifier();
|
||||
|
||||
/**
|
||||
* Get the client that the token was issued to.
|
||||
*
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
public function getClient();
|
||||
|
||||
/**
|
||||
* Set the client that the token was issued to.
|
||||
*
|
||||
* @param ClientEntityInterface $client
|
||||
*/
|
||||
public function setClient(ClientEntityInterface $client);
|
||||
|
||||
/**
|
||||
* Associate a scope with the token.
|
||||
*
|
||||
* @param ScopeEntityInterface $scope
|
||||
*/
|
||||
public function addScope(ScopeEntityInterface $scope);
|
||||
|
||||
/**
|
||||
* Return an array of scopes associated with the token.
|
||||
*
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes();
|
||||
}
|
84
src/Entities/Traits/AccessTokenTrait.php
Normal file
84
src/Entities/Traits/AccessTokenTrait.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Lcobucci\JWT\Token;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
trait AccessTokenTrait
|
||||
{
|
||||
/**
|
||||
* @var CryptKey
|
||||
*/
|
||||
private $privateKey;
|
||||
|
||||
/**
|
||||
* Set the private key used to encrypt this access token.
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $privateKey)
|
||||
{
|
||||
$this->privateKey = $privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JWT from the access token
|
||||
*
|
||||
* @param CryptKey $privateKey
|
||||
*
|
||||
* @return Token
|
||||
*/
|
||||
private function convertToJWT(CryptKey $privateKey)
|
||||
{
|
||||
return (new Builder())
|
||||
->setAudience($this->getClient()->getIdentifier())
|
||||
->setId($this->getIdentifier())
|
||||
->setIssuedAt(time())
|
||||
->setNotBefore(time())
|
||||
->setExpiration($this->getExpiryDateTime()->getTimestamp())
|
||||
->setSubject((string) $this->getUserIdentifier())
|
||||
->set('scopes', $this->getScopes())
|
||||
->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase()))
|
||||
->getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a string representation from the access token
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->convertToJWT($this->privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
abstract public function getClient();
|
||||
|
||||
/**
|
||||
* @return DateTimeImmutable
|
||||
*/
|
||||
abstract public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* @return string|int
|
||||
*/
|
||||
abstract public function getUserIdentifier();
|
||||
|
||||
/**
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
abstract public function getScopes();
|
||||
}
|
34
src/Entities/Traits/AuthCodeTrait.php
Normal file
34
src/Entities/Traits/AuthCodeTrait.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
trait AuthCodeTrait
|
||||
{
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
protected $redirectUri;
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRedirectUri()
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
*/
|
||||
public function setRedirectUri($uri)
|
||||
{
|
||||
$this->redirectUri = $uri;
|
||||
}
|
||||
}
|
61
src/Entities/Traits/ClientTrait.php
Normal file
61
src/Entities/Traits/ClientTrait.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
trait ClientTrait
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string|string[]
|
||||
*/
|
||||
protected $redirectUri;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isConfidential = false;
|
||||
|
||||
/**
|
||||
* Get the client's name.
|
||||
*
|
||||
* @return string
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered redirect URI (as a string).
|
||||
*
|
||||
* Alternatively return an indexed array of redirect URIs.
|
||||
*
|
||||
* @return string|string[]
|
||||
*/
|
||||
public function getRedirectUri()
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the client is confidential.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConfidential()
|
||||
{
|
||||
return $this->isConfidential;
|
||||
}
|
||||
}
|
34
src/Entities/Traits/EntityTrait.php
Normal file
34
src/Entities/Traits/EntityTrait.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
trait EntityTrait
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $identifier
|
||||
*/
|
||||
public function setIdentifier($identifier)
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
}
|
||||
}
|
62
src/Entities/Traits/RefreshTokenTrait.php
Normal file
62
src/Entities/Traits/RefreshTokenTrait.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
|
||||
trait RefreshTokenTrait
|
||||
{
|
||||
/**
|
||||
* @var AccessTokenEntityInterface
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var DateTimeImmutable
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAccessToken(AccessTokenEntityInterface $accessToken)
|
||||
{
|
||||
$this->accessToken = $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAccessToken()
|
||||
{
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return DateTimeImmutable
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
return $this->expiryDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param DateTimeImmutable $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(DateTimeImmutable $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
}
|
28
src/Entities/Traits/ScopeTrait.php
Normal file
28
src/Entities/Traits/ScopeTrait.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Andrew Millington <andrew@noexceptions.io>
|
||||
* @copyright Copyright (c) Andrew Millington
|
||||
* @license http://mit-license.org
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
trait ScopeTrait
|
||||
{
|
||||
/**
|
||||
* Serialize the object to the scopes string identifier when using json_encode().
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->getIdentifier();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getIdentifier();
|
||||
}
|
117
src/Entities/Traits/TokenEntityTrait.php
Normal file
117
src/Entities/Traits/TokenEntityTrait.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
trait TokenEntityTrait
|
||||
{
|
||||
/**
|
||||
* @var ScopeEntityInterface[]
|
||||
*/
|
||||
protected $scopes = [];
|
||||
|
||||
/**
|
||||
* @var DateTimeImmutable
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
/**
|
||||
* @var string|int|null
|
||||
*/
|
||||
protected $userIdentifier;
|
||||
|
||||
/**
|
||||
* @var ClientEntityInterface
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Associate a scope with the token.
|
||||
*
|
||||
* @param ScopeEntityInterface $scope
|
||||
*/
|
||||
public function addScope(ScopeEntityInterface $scope)
|
||||
{
|
||||
$this->scopes[$scope->getIdentifier()] = $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of scopes associated with the token.
|
||||
*
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
return array_values($this->scopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return DateTimeImmutable
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
return $this->expiryDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param DateTimeImmutable $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(DateTimeImmutable $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the identifier of the user associated with the token.
|
||||
*
|
||||
* @param string|int|null $identifier The identifier of the user
|
||||
*/
|
||||
public function setUserIdentifier($identifier)
|
||||
{
|
||||
$this->userIdentifier = $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token user's identifier.
|
||||
*
|
||||
* @return string|int|null
|
||||
*/
|
||||
public function getUserIdentifier()
|
||||
{
|
||||
return $this->userIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client that the token was issued to.
|
||||
*
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the client that the token was issued to.
|
||||
*
|
||||
* @param ClientEntityInterface $client
|
||||
*/
|
||||
public function setClient(ClientEntityInterface $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
}
|
20
src/Entities/UserEntityInterface.php
Normal file
20
src/Entities/UserEntityInterface.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Entities;
|
||||
|
||||
interface UserEntityInterface
|
||||
{
|
||||
/**
|
||||
* Return the user's identifier.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getIdentifier();
|
||||
}
|
399
src/Exception/OAuthServerException.php
Normal file
399
src/Exception/OAuthServerException.php
Normal file
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
use Exception;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Throwable;
|
||||
|
||||
class OAuthServerException extends Exception
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $httpStatusCode;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $errorType;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $hint;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $redirectUri;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
private $serverRequest;
|
||||
|
||||
/**
|
||||
* Throw a new exception.
|
||||
*
|
||||
* @param string $message Error message
|
||||
* @param int $code Error code
|
||||
* @param string $errorType Error type
|
||||
* @param int $httpStatusCode HTTP status code to send (default = 400)
|
||||
* @param null|string $hint A helper hint
|
||||
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||
* @param Throwable $previous Previous exception
|
||||
*/
|
||||
public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->httpStatusCode = $httpStatusCode;
|
||||
$this->errorType = $errorType;
|
||||
$this->hint = $hint;
|
||||
$this->redirectUri = $redirectUri;
|
||||
$this->payload = [
|
||||
'error' => $errorType,
|
||||
'error_description' => $message,
|
||||
];
|
||||
if ($hint !== null) {
|
||||
$this->payload['hint'] = $hint;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current payload.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPayload()
|
||||
{
|
||||
$payload = $this->payload;
|
||||
|
||||
// The "message" property is deprecated and replaced by "error_description"
|
||||
// TODO: remove "message" property
|
||||
if (isset($payload['error_description']) && !isset($payload['message'])) {
|
||||
$payload['message'] = $payload['error_description'];
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current payload.
|
||||
*
|
||||
* @param array $payload
|
||||
*/
|
||||
public function setPayload(array $payload)
|
||||
{
|
||||
$this->payload = $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the server request that is responsible for generating the exception
|
||||
*
|
||||
* @param ServerRequestInterface $serverRequest
|
||||
*/
|
||||
public function setServerRequest(ServerRequestInterface $serverRequest)
|
||||
{
|
||||
$this->serverRequest = $serverRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported grant type error.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function unsupportedGrantType()
|
||||
{
|
||||
$errorMessage = 'The authorization grant type is not supported by the authorization server.';
|
||||
$hint = 'Check that all required parameters have been provided';
|
||||
|
||||
return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid request error.
|
||||
*
|
||||
* @param string $parameter The invalid parameter
|
||||
* @param null|string $hint
|
||||
* @param Throwable $previous Previous exception
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidRequest($parameter, $hint = null, Throwable $previous = null)
|
||||
{
|
||||
$errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
|
||||
'includes a parameter more than once, or is otherwise malformed.';
|
||||
$hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
|
||||
|
||||
return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid client error.
|
||||
*
|
||||
* @param ServerRequestInterface $serverRequest
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidClient(ServerRequestInterface $serverRequest)
|
||||
{
|
||||
$exception = new static('Client authentication failed', 4, 'invalid_client', 401);
|
||||
|
||||
$exception->setServerRequest($serverRequest);
|
||||
|
||||
return $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid scope error.
|
||||
*
|
||||
* @param string $scope The bad scope
|
||||
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidScope($scope, $redirectUri = null)
|
||||
{
|
||||
$errorMessage = 'The requested scope is invalid, unknown, or malformed';
|
||||
|
||||
if (empty($scope)) {
|
||||
$hint = 'Specify a scope in the request or set a default scope';
|
||||
} else {
|
||||
$hint = sprintf(
|
||||
'Check the `%s` scope',
|
||||
htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
|
||||
);
|
||||
}
|
||||
|
||||
return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid credentials error.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidCredentials()
|
||||
{
|
||||
return new static('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server error.
|
||||
*
|
||||
* @param string $hint
|
||||
* @param Throwable $previous
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function serverError($hint, Throwable $previous = null)
|
||||
{
|
||||
return new static(
|
||||
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
|
||||
. ' the request: ' . $hint,
|
||||
7,
|
||||
'server_error',
|
||||
500,
|
||||
null,
|
||||
null,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid refresh token.
|
||||
*
|
||||
* @param null|string $hint
|
||||
* @param Throwable $previous
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidRefreshToken($hint = null, Throwable $previous = null)
|
||||
{
|
||||
return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint, null, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access denied.
|
||||
*
|
||||
* @param null|string $hint
|
||||
* @param null|string $redirectUri
|
||||
* @param Throwable $previous
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null)
|
||||
{
|
||||
return new static(
|
||||
'The resource owner or authorization server denied the request.',
|
||||
9,
|
||||
'access_denied',
|
||||
401,
|
||||
$hint,
|
||||
$redirectUri,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid grant.
|
||||
*
|
||||
* @param string $hint
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidGrant($hint = '')
|
||||
{
|
||||
return new static(
|
||||
'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token '
|
||||
. 'is invalid, expired, revoked, does not match the redirection URI used in the authorization request, '
|
||||
. 'or was issued to another client.',
|
||||
10,
|
||||
'invalid_grant',
|
||||
400,
|
||||
$hint
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorType()
|
||||
{
|
||||
return $this->errorType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a HTTP response.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
|
||||
* @param int $jsonOptions options passed to json_encode
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
|
||||
{
|
||||
$headers = $this->getHttpHeaders();
|
||||
|
||||
$payload = $this->getPayload();
|
||||
|
||||
$redirectUri = $this->getRedirectUri($useFragment);
|
||||
if ($redirectUri !== null) {
|
||||
return $response->withStatus(302)->withHeader('Location', $redirectUri);
|
||||
}
|
||||
|
||||
foreach ($headers as $header => $content) {
|
||||
$response = $response->withHeader($header, $content);
|
||||
}
|
||||
|
||||
$responseBody = json_encode($payload, $jsonOptions) ?: 'JSON encoding of payload failed';
|
||||
|
||||
$response->getBody()->write($responseBody);
|
||||
|
||||
return $response->withStatus($this->getHttpStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers that have to be send with the error response.
|
||||
*
|
||||
* @return array Array with header values
|
||||
*/
|
||||
public function getHttpHeaders()
|
||||
{
|
||||
$headers = [
|
||||
'Content-type' => 'application/json',
|
||||
];
|
||||
|
||||
// Add "WWW-Authenticate" header
|
||||
//
|
||||
// RFC 6749, section 5.2.:
|
||||
// "If the client attempted to authenticate via the 'Authorization'
|
||||
// request header field, the authorization server MUST
|
||||
// respond with an HTTP 401 (Unauthorized) status code and
|
||||
// include the "WWW-Authenticate" response header field
|
||||
// matching the authentication scheme used by the client.
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($this->errorType === 'invalid_client' && $this->serverRequest->hasHeader('Authorization') === true) {
|
||||
$authScheme = strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic';
|
||||
|
||||
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the exception has an associated redirect URI.
|
||||
*
|
||||
* Returns whether the exception includes a redirect, since
|
||||
* getHttpStatusCode() doesn't return a 302 when there's a
|
||||
* redirect enabled. This helps when you want to override local
|
||||
* error pages but want to let redirects through.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRedirect()
|
||||
{
|
||||
return $this->redirectUri !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the redirectUri with all necessary args.
|
||||
*
|
||||
* Null will be returned if the exception doesn't contain the redirectUri.
|
||||
*
|
||||
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRedirectUri(bool $useFragment = false): ?string
|
||||
{
|
||||
if ($this->redirectUri === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$redirectUri = $this->redirectUri;
|
||||
if ($useFragment) {
|
||||
$redirectUri .= strpos($this->redirectUri, '#') === false ? '#' : '&';
|
||||
} else {
|
||||
$redirectUri .= strpos($this->redirectUri, '?') === false ? '?' : '&';
|
||||
}
|
||||
|
||||
return $redirectUri . http_build_query($this->getPayload());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP status code to send when the exceptions is output.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHttpStatusCode()
|
||||
{
|
||||
return $this->httpStatusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
public function getHint()
|
||||
{
|
||||
return $this->hint;
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Ivan Kurnosov <zerkms@zerkms.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
|
||||
{
|
||||
/**
|
||||
* @return UniqueTokenIdentifierConstraintViolationException
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
$errorMessage = 'Could not create unique access token identifier';
|
||||
|
||||
return new static($errorMessage, 100, 'access_token_duplicate', 500);
|
||||
}
|
||||
}
|
29
src/Grant/AbstractAuthorizeGrant.php
Normal file
29
src/Grant/AbstractAuthorizeGrant.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract authorization grant.
|
||||
*
|
||||
* @author Julián Gutiérrez <juliangut@gmail.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
abstract class AbstractAuthorizeGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @param string $uri
|
||||
* @param array $params
|
||||
* @param string $queryDelimiter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function makeRedirectUri($uri, $params = [], $queryDelimiter = '?')
|
||||
{
|
||||
$uri .= (strstr($uri, $queryDelimiter) === false) ? $queryDelimiter : '&';
|
||||
|
||||
return $uri . http_build_query($params);
|
||||
}
|
||||
}
|
599
src/Grant/AbstractGrant.php
Normal file
599
src/Grant/AbstractGrant.php
Normal file
@@ -0,0 +1,599 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Abstract grant.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use Error;
|
||||
use Exception;
|
||||
use League\Event\EmitterAwareTrait;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\CryptTrait;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Abstract grant class.
|
||||
*/
|
||||
abstract class AbstractGrant implements GrantTypeInterface
|
||||
{
|
||||
use EmitterAwareTrait, CryptTrait;
|
||||
|
||||
const SCOPE_DELIMITER_STRING = ' ';
|
||||
|
||||
const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
|
||||
|
||||
/**
|
||||
* @var ClientRepositoryInterface
|
||||
*/
|
||||
protected $clientRepository;
|
||||
|
||||
/**
|
||||
* @var AccessTokenRepositoryInterface
|
||||
*/
|
||||
protected $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* @var ScopeRepositoryInterface
|
||||
*/
|
||||
protected $scopeRepository;
|
||||
|
||||
/**
|
||||
* @var AuthCodeRepositoryInterface
|
||||
*/
|
||||
protected $authCodeRepository;
|
||||
|
||||
/**
|
||||
* @var RefreshTokenRepositoryInterface
|
||||
*/
|
||||
protected $refreshTokenRepository;
|
||||
|
||||
/**
|
||||
* @var UserRepositoryInterface
|
||||
*/
|
||||
protected $userRepository;
|
||||
|
||||
/**
|
||||
* @var DateInterval
|
||||
*/
|
||||
protected $refreshTokenTTL;
|
||||
|
||||
/**
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* @string
|
||||
*/
|
||||
protected $defaultScope;
|
||||
|
||||
/**
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
*/
|
||||
public function setClientRepository(ClientRepositoryInterface $clientRepository)
|
||||
{
|
||||
$this->clientRepository = $clientRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
|
||||
{
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
*/
|
||||
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
|
||||
{
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*/
|
||||
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||
{
|
||||
$this->refreshTokenRepository = $refreshTokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||
*/
|
||||
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
|
||||
{
|
||||
$this->authCodeRepository = $authCodeRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserRepositoryInterface $userRepository
|
||||
*/
|
||||
public function setUserRepository(UserRepositoryInterface $userRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
|
||||
{
|
||||
$this->refreshTokenTTL = $refreshTokenTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the private key
|
||||
*
|
||||
* @param CryptKey $key
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $key)
|
||||
{
|
||||
$this->privateKey = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scope
|
||||
*/
|
||||
public function setDefaultScope($scope)
|
||||
{
|
||||
$this->defaultScope = $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the client.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
protected function validateClient(ServerRequestInterface $request)
|
||||
{
|
||||
list($clientId, $clientSecret) = $this->getClientCredentials($request);
|
||||
|
||||
if ($this->clientRepository->validateClient($clientId, $clientSecret, $this->getIdentifier()) === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
|
||||
throw OAuthServerException::invalidClient($request);
|
||||
}
|
||||
|
||||
$client = $this->getClientEntityOrFail($clientId, $request);
|
||||
|
||||
// If a redirect URI is provided ensure it matches what is pre-registered
|
||||
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$this->validateRedirectUri($redirectUri, $client, $request);
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around ClientRepository::getClientEntity() that ensures we emit
|
||||
* an event and throw an exception if the repo doesn't return a client
|
||||
* entity.
|
||||
*
|
||||
* This is a bit of defensive coding because the interface contract
|
||||
* doesn't actually enforce non-null returns/exception-on-no-client so
|
||||
* getClientEntity might return null. By contrast, this method will
|
||||
* always either return a ClientEntityInterface or throw.
|
||||
*
|
||||
* @param string $clientId
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
protected function getClientEntityOrFail($clientId, ServerRequestInterface $request)
|
||||
{
|
||||
$client = $this->clientRepository->getClientEntity($clientId);
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient($request);
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the client credentials from the request from the request body or
|
||||
* the Http Basic Authorization header
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getClientCredentials(ServerRequestInterface $request)
|
||||
{
|
||||
list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
|
||||
|
||||
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
|
||||
|
||||
if (is_null($clientId)) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
}
|
||||
|
||||
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
|
||||
|
||||
return [$clientId, $clientSecret];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate redirectUri from the request.
|
||||
* If a redirect URI is provided ensure it matches what is pre-registered
|
||||
*
|
||||
* @param string $redirectUri
|
||||
* @param ClientEntityInterface $client
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*/
|
||||
protected function validateRedirectUri(
|
||||
string $redirectUri,
|
||||
ClientEntityInterface $client,
|
||||
ServerRequestInterface $request
|
||||
) {
|
||||
if (\is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient($request);
|
||||
} elseif (\is_array($client->getRedirectUri())
|
||||
&& \in_array($redirectUri, $client->getRedirectUri(), true) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient($request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate scopes in the request.
|
||||
*
|
||||
* @param string|array $scopes
|
||||
* @param string $redirectUri
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function validateScopes($scopes, $redirectUri = null)
|
||||
{
|
||||
if (!\is_array($scopes)) {
|
||||
$scopes = $this->convertScopesQueryStringToArray($scopes);
|
||||
}
|
||||
|
||||
$validScopes = [];
|
||||
|
||||
foreach ($scopes as $scopeItem) {
|
||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
|
||||
|
||||
if ($scope instanceof ScopeEntityInterface === false) {
|
||||
throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
|
||||
}
|
||||
|
||||
$validScopes[] = $scope;
|
||||
}
|
||||
|
||||
return $validScopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a scopes query string to an array to easily iterate for validation.
|
||||
*
|
||||
* @param string $scopes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function convertScopesQueryStringToArray($scopes)
|
||||
{
|
||||
return array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), function ($scope) {
|
||||
return !empty($scope);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve request parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
{
|
||||
$requestParameters = (array) $request->getParsedBody();
|
||||
|
||||
return $requestParameters[$parameter] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve HTTP Basic Auth credentials with the Authorization header
|
||||
* of a request. First index of the returned array is the username,
|
||||
* second is the password (so list() will work). If the header does
|
||||
* not exist, or is otherwise an invalid HTTP Basic header, return
|
||||
* [null, null].
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return string[]|null[]
|
||||
*/
|
||||
protected function getBasicAuthCredentials(ServerRequestInterface $request)
|
||||
{
|
||||
if (!$request->hasHeader('Authorization')) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
$header = $request->getHeader('Authorization')[0];
|
||||
if (strpos($header, 'Basic ') !== 0) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if (!($decoded = base64_decode(substr($header, 6)))) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if (strpos($decoded, ':') === false) {
|
||||
return [null, null]; // HTTP Basic header without colon isn't valid
|
||||
}
|
||||
|
||||
return explode(':', $decoded, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve query string parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
{
|
||||
return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve cookie parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
{
|
||||
return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve server parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||
{
|
||||
return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an access token.
|
||||
*
|
||||
* @param DateInterval $accessTokenTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string|null $userIdentifier
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
protected function issueAccessToken(
|
||||
DateInterval $accessTokenTTL,
|
||||
ClientEntityInterface $client,
|
||||
$userIdentifier,
|
||||
array $scopes = []
|
||||
) {
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
|
||||
$accessToken->setExpiryDateTime((new DateTimeImmutable())->add($accessTokenTTL));
|
||||
$accessToken->setPrivateKey($this->privateKey);
|
||||
|
||||
while ($maxGenerationAttempts-- > 0) {
|
||||
$accessToken->setIdentifier($this->generateUniqueIdentifier());
|
||||
try {
|
||||
$this->accessTokenRepository->persistNewAccessToken($accessToken);
|
||||
|
||||
return $accessToken;
|
||||
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||
if ($maxGenerationAttempts === 0) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an auth code.
|
||||
*
|
||||
* @param DateInterval $authCodeTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string $userIdentifier
|
||||
* @param string|null $redirectUri
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return AuthCodeEntityInterface
|
||||
*/
|
||||
protected function issueAuthCode(
|
||||
DateInterval $authCodeTTL,
|
||||
ClientEntityInterface $client,
|
||||
$userIdentifier,
|
||||
$redirectUri,
|
||||
array $scopes = []
|
||||
) {
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$authCode = $this->authCodeRepository->getNewAuthCode();
|
||||
$authCode->setExpiryDateTime((new DateTimeImmutable())->add($authCodeTTL));
|
||||
$authCode->setClient($client);
|
||||
$authCode->setUserIdentifier($userIdentifier);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$authCode->setRedirectUri($redirectUri);
|
||||
}
|
||||
|
||||
foreach ($scopes as $scope) {
|
||||
$authCode->addScope($scope);
|
||||
}
|
||||
|
||||
while ($maxGenerationAttempts-- > 0) {
|
||||
$authCode->setIdentifier($this->generateUniqueIdentifier());
|
||||
try {
|
||||
$this->authCodeRepository->persistNewAuthCode($authCode);
|
||||
|
||||
return $authCode;
|
||||
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||
if ($maxGenerationAttempts === 0) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return RefreshTokenEntityInterface|null
|
||||
*/
|
||||
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
|
||||
{
|
||||
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
||||
|
||||
if ($refreshToken === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$refreshToken->setExpiryDateTime((new DateTimeImmutable())->add($this->refreshTokenTTL));
|
||||
$refreshToken->setAccessToken($accessToken);
|
||||
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
while ($maxGenerationAttempts-- > 0) {
|
||||
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
|
||||
try {
|
||||
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
|
||||
|
||||
return $refreshToken;
|
||||
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||
if ($maxGenerationAttempts === 0) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new unique identifier.
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateUniqueIdentifier($length = 40)
|
||||
{
|
||||
try {
|
||||
return bin2hex(random_bytes($length));
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (TypeError $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
|
||||
} catch (Error $e) {
|
||||
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
|
||||
} catch (Exception $e) {
|
||||
// If you get this message, the CSPRNG failed hard.
|
||||
throw OAuthServerException::serverError('Could not generate a random string', $e);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
|
||||
{
|
||||
$requestParameters = (array) $request->getParsedBody();
|
||||
|
||||
return (
|
||||
array_key_exists('grant_type', $requestParameters)
|
||||
&& $requestParameters['grant_type'] === $this->getIdentifier()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
throw new LogicException('This grant cannot validate an authorization request');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
throw new LogicException('This grant cannot complete an authorization request');
|
||||
}
|
||||
}
|
401
src/Grant/AuthCodeGrant.php
Normal file
401
src/Grant/AuthCodeGrant.php
Normal file
@@ -0,0 +1,401 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use Exception;
|
||||
use League\OAuth2\Server\CodeChallengeVerifiers\CodeChallengeVerifierInterface;
|
||||
use League\OAuth2\Server\CodeChallengeVerifiers\PlainVerifier;
|
||||
use League\OAuth2\Server\CodeChallengeVerifiers\S256Verifier;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use stdClass;
|
||||
|
||||
class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
{
|
||||
/**
|
||||
* @var DateInterval
|
||||
*/
|
||||
private $authCodeTTL;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $requireCodeChallengeForPublicClients = true;
|
||||
|
||||
/**
|
||||
* @var CodeChallengeVerifierInterface[]
|
||||
*/
|
||||
private $codeChallengeVerifiers = [];
|
||||
|
||||
/**
|
||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param DateInterval $authCodeTTL
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(
|
||||
AuthCodeRepositoryInterface $authCodeRepository,
|
||||
RefreshTokenRepositoryInterface $refreshTokenRepository,
|
||||
DateInterval $authCodeTTL
|
||||
) {
|
||||
$this->setAuthCodeRepository($authCodeRepository);
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
$this->authCodeTTL = $authCodeTTL;
|
||||
$this->refreshTokenTTL = new DateInterval('P1M');
|
||||
|
||||
if (in_array('sha256', hash_algos(), true)) {
|
||||
$s256Verifier = new S256Verifier();
|
||||
$this->codeChallengeVerifiers[$s256Verifier->getMethod()] = $s256Verifier;
|
||||
}
|
||||
|
||||
$plainVerifier = new PlainVerifier();
|
||||
$this->codeChallengeVerifiers[$plainVerifier->getMethod()] = $plainVerifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the requirement for a code challenge for public clients.
|
||||
*/
|
||||
public function disableRequireCodeChallengeForPublicClients()
|
||||
{
|
||||
$this->requireCodeChallengeForPublicClients = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to an access token request.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param DateInterval $accessTokenTTL
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
list($clientId) = $this->getClientCredentials($request);
|
||||
|
||||
$client = $this->getClientEntityOrFail($clientId, $request);
|
||||
|
||||
// Only validate the client if it is confidential
|
||||
if ($client->isConfidential()) {
|
||||
$this->validateClient($request);
|
||||
}
|
||||
|
||||
$encryptedAuthCode = $this->getRequestParameter('code', $request, null);
|
||||
|
||||
if ($encryptedAuthCode === null) {
|
||||
throw OAuthServerException::invalidRequest('code');
|
||||
}
|
||||
|
||||
try {
|
||||
$authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
|
||||
|
||||
$this->validateAuthorizationCode($authCodePayload, $client, $request);
|
||||
|
||||
$scopes = $this->scopeRepository->finalizeScopes(
|
||||
$this->validateScopes($authCodePayload->scopes),
|
||||
$this->getIdentifier(),
|
||||
$client,
|
||||
$authCodePayload->user_id
|
||||
);
|
||||
} catch (LogicException $e) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code', $e);
|
||||
}
|
||||
|
||||
// Validate code challenge
|
||||
if (!empty($authCodePayload->code_challenge)) {
|
||||
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null);
|
||||
|
||||
if ($codeVerifier === null) {
|
||||
throw OAuthServerException::invalidRequest('code_verifier');
|
||||
}
|
||||
|
||||
// Validate code_verifier according to RFC-7636
|
||||
// @see: https://tools.ietf.org/html/rfc7636#section-4.1
|
||||
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeVerifier) !== 1) {
|
||||
throw OAuthServerException::invalidRequest(
|
||||
'code_verifier',
|
||||
'Code Verifier must follow the specifications of RFC-7636.'
|
||||
);
|
||||
}
|
||||
|
||||
if (property_exists($authCodePayload, 'code_challenge_method')) {
|
||||
if (isset($this->codeChallengeVerifiers[$authCodePayload->code_challenge_method])) {
|
||||
$codeChallengeVerifier = $this->codeChallengeVerifiers[$authCodePayload->code_challenge_method];
|
||||
|
||||
if ($codeChallengeVerifier->verifyCodeChallenge($codeVerifier, $authCodePayload->code_challenge) === false) {
|
||||
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
|
||||
}
|
||||
} else {
|
||||
throw OAuthServerException::serverError(
|
||||
sprintf(
|
||||
'Unsupported code challenge method `%s`',
|
||||
$authCodePayload->code_challenge_method
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue and persist new access token
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||
$responseType->setAccessToken($accessToken);
|
||||
|
||||
// Issue and persist new refresh token if given
|
||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||
|
||||
if ($refreshToken !== null) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||
$responseType->setRefreshToken($refreshToken);
|
||||
}
|
||||
|
||||
// Revoke used auth code
|
||||
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
|
||||
|
||||
return $responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the authorization code.
|
||||
*
|
||||
* @param stdClass $authCodePayload
|
||||
* @param ClientEntityInterface $client
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
private function validateAuthorizationCode(
|
||||
$authCodePayload,
|
||||
ClientEntityInterface $client,
|
||||
ServerRequestInterface $request
|
||||
) {
|
||||
if (time() > $authCodePayload->expire_time) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code has expired');
|
||||
}
|
||||
|
||||
if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code has been revoked');
|
||||
}
|
||||
|
||||
if ($authCodePayload->client_id !== $client->getIdentifier()) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Authorization code was not issued to this client');
|
||||
}
|
||||
|
||||
// The redirect URI is required in this request
|
||||
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
|
||||
if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) {
|
||||
throw OAuthServerException::invalidRequest('redirect_uri');
|
||||
}
|
||||
|
||||
if ($authCodePayload->redirect_uri !== $redirectUri) {
|
||||
throw OAuthServerException::invalidRequest('redirect_uri', 'Invalid redirect URI');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grant identifier that can be used in matching up requests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'authorization_code';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
return (
|
||||
array_key_exists('response_type', $request->getQueryParams())
|
||||
&& $request->getQueryParams()['response_type'] === 'code'
|
||||
&& isset($request->getQueryParams()['client_id'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
$clientId = $this->getQueryStringParameter(
|
||||
'client_id',
|
||||
$request,
|
||||
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||
);
|
||||
|
||||
if ($clientId === null) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
}
|
||||
|
||||
$client = $this->getClientEntityOrFail($clientId, $request);
|
||||
|
||||
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$this->validateRedirectUri($redirectUri, $client, $request);
|
||||
} elseif (empty($client->getRedirectUri()) ||
|
||||
(\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1)) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient($request);
|
||||
} else {
|
||||
$redirectUri = \is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri();
|
||||
}
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
|
||||
$redirectUri
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
|
||||
$authorizationRequest = new AuthorizationRequest();
|
||||
$authorizationRequest->setGrantTypeId($this->getIdentifier());
|
||||
$authorizationRequest->setClient($client);
|
||||
$authorizationRequest->setRedirectUri($redirectUri);
|
||||
|
||||
if ($stateParameter !== null) {
|
||||
$authorizationRequest->setState($stateParameter);
|
||||
}
|
||||
|
||||
$authorizationRequest->setScopes($scopes);
|
||||
|
||||
$codeChallenge = $this->getQueryStringParameter('code_challenge', $request);
|
||||
|
||||
if ($codeChallenge !== null) {
|
||||
$codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
|
||||
|
||||
if (array_key_exists($codeChallengeMethod, $this->codeChallengeVerifiers) === false) {
|
||||
throw OAuthServerException::invalidRequest(
|
||||
'code_challenge_method',
|
||||
'Code challenge method must be one of ' . implode(', ', array_map(
|
||||
function ($method) {
|
||||
return '`' . $method . '`';
|
||||
},
|
||||
array_keys($this->codeChallengeVerifiers)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
// Validate code_challenge according to RFC-7636
|
||||
// @see: https://tools.ietf.org/html/rfc7636#section-4.2
|
||||
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
|
||||
throw OAuthServerException::invalidRequest(
|
||||
'code_challenged',
|
||||
'Code challenge must follow the specifications of RFC-7636.'
|
||||
);
|
||||
}
|
||||
|
||||
$authorizationRequest->setCodeChallenge($codeChallenge);
|
||||
$authorizationRequest->setCodeChallengeMethod($codeChallengeMethod);
|
||||
} elseif ($this->requireCodeChallengeForPublicClients && !$client->isConfidential()) {
|
||||
throw OAuthServerException::invalidRequest('code_challenge', 'Code challenge must be provided for public clients');
|
||||
}
|
||||
|
||||
return $authorizationRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||
throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||
}
|
||||
|
||||
$finalRedirectUri = $authorizationRequest->getRedirectUri()
|
||||
?? $this->getClientRedirectUri($authorizationRequest);
|
||||
|
||||
// The user approved the client, redirect them back with an auth code
|
||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||
$authCode = $this->issueAuthCode(
|
||||
$this->authCodeTTL,
|
||||
$authorizationRequest->getClient(),
|
||||
$authorizationRequest->getUser()->getIdentifier(),
|
||||
$authorizationRequest->getRedirectUri(),
|
||||
$authorizationRequest->getScopes()
|
||||
);
|
||||
|
||||
$payload = [
|
||||
'client_id' => $authCode->getClient()->getIdentifier(),
|
||||
'redirect_uri' => $authCode->getRedirectUri(),
|
||||
'auth_code_id' => $authCode->getIdentifier(),
|
||||
'scopes' => $authCode->getScopes(),
|
||||
'user_id' => $authCode->getUserIdentifier(),
|
||||
'expire_time' => (new DateTimeImmutable())->add($this->authCodeTTL)->getTimestamp(),
|
||||
'code_challenge' => $authorizationRequest->getCodeChallenge(),
|
||||
'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(),
|
||||
];
|
||||
|
||||
$jsonPayload = json_encode($payload);
|
||||
|
||||
if ($jsonPayload === false) {
|
||||
throw new LogicException('An error was encountered when JSON encoding the authorization request response');
|
||||
}
|
||||
|
||||
$response = new RedirectResponse();
|
||||
$response->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'code' => $this->encrypt($jsonPayload),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// The user denied the client, redirect them back with an error
|
||||
throw OAuthServerException::accessDenied(
|
||||
'The user denied the request',
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client redirect URI if not set in the request.
|
||||
*
|
||||
* @param AuthorizationRequest $authorizationRequest
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getClientRedirectUri(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
return \is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||
: $authorizationRequest->getClient()->getRedirectUri();
|
||||
}
|
||||
}
|
70
src/Grant/ClientCredentialsGrant.php
Normal file
70
src/Grant/ClientCredentialsGrant.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Client credentials grant.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Client credentials grant class.
|
||||
*/
|
||||
class ClientCredentialsGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
list($clientId) = $this->getClientCredentials($request);
|
||||
|
||||
$client = $this->getClientEntityOrFail($clientId, $request);
|
||||
|
||||
if (!$client->isConfidential()) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
|
||||
throw OAuthServerException::invalidClient($request);
|
||||
}
|
||||
|
||||
// Validate request
|
||||
$this->validateClient($request);
|
||||
|
||||
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
|
||||
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client);
|
||||
|
||||
// Issue and persist access token
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $finalizedScopes);
|
||||
|
||||
// Send event to emitter
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||
|
||||
// Inject access token into response type
|
||||
$responseType->setAccessToken($accessToken);
|
||||
|
||||
return $responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'client_credentials';
|
||||
}
|
||||
}
|
144
src/Grant/GrantTypeInterface.php
Normal file
144
src/Grant/GrantTypeInterface.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Grant type interface.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use Defuse\Crypto\Key;
|
||||
use League\Event\EmitterAwareInterface;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Grant type interface.
|
||||
*/
|
||||
interface GrantTypeInterface extends EmitterAwareInterface
|
||||
{
|
||||
/**
|
||||
* Set refresh token TTL.
|
||||
*
|
||||
* @param DateInterval $refreshTokenTTL
|
||||
*/
|
||||
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL);
|
||||
|
||||
/**
|
||||
* Return the grant identifier that can be used in matching up requests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier();
|
||||
|
||||
/**
|
||||
* Respond to an incoming request.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $accessTokenTTL
|
||||
);
|
||||
|
||||
/**
|
||||
* The grant type should return true if it is able to response to an authorization request
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request);
|
||||
|
||||
/**
|
||||
* If the grant can respond to an authorization request this method should be called to validate the parameters of
|
||||
* the request.
|
||||
*
|
||||
* If the validation is successful an AuthorizationRequest object will be returned. This object can be safely
|
||||
* serialized in a user's session, and can be used during user authentication and authorization.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return AuthorizationRequest
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request);
|
||||
|
||||
/**
|
||||
* Once a user has authenticated and authorized the client the grant can complete the authorization request.
|
||||
* The AuthorizationRequest object's $userId property must be set to the authenticated user and the
|
||||
* $authorizationApproved property must reflect their desire to authorize or deny the client.
|
||||
*
|
||||
* @param AuthorizationRequest $authorizationRequest
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest);
|
||||
|
||||
/**
|
||||
* The grant type should return true if it is able to respond to this request.
|
||||
*
|
||||
* For example most grant types will check that the $_POST['grant_type'] property matches it's identifier property.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRespondToAccessTokenRequest(ServerRequestInterface $request);
|
||||
|
||||
/**
|
||||
* Set the client repository.
|
||||
*
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
*/
|
||||
public function setClientRepository(ClientRepositoryInterface $clientRepository);
|
||||
|
||||
/**
|
||||
* Set the access token repository.
|
||||
*
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository);
|
||||
|
||||
/**
|
||||
* Set the scope repository.
|
||||
*
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
*/
|
||||
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository);
|
||||
|
||||
/**
|
||||
* Set the default scope.
|
||||
*
|
||||
* @param string $scope
|
||||
*/
|
||||
public function setDefaultScope($scope);
|
||||
|
||||
/**
|
||||
* Set the path to the private key.
|
||||
*
|
||||
* @param CryptKey $privateKey
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $privateKey);
|
||||
|
||||
/**
|
||||
* Set the encryption key
|
||||
*
|
||||
* @param string|Key|null $key
|
||||
*/
|
||||
public function setEncryptionKey($key = null);
|
||||
}
|
224
src/Grant/ImplicitGrant.php
Normal file
224
src/Grant/ImplicitGrant.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
{
|
||||
/**
|
||||
* @var DateInterval
|
||||
*/
|
||||
private $accessTokenTTL;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $queryDelimiter;
|
||||
|
||||
/**
|
||||
* @param DateInterval $accessTokenTTL
|
||||
* @param string $queryDelimiter
|
||||
*/
|
||||
public function __construct(DateInterval $accessTokenTTL, $queryDelimiter = '#')
|
||||
{
|
||||
$this->accessTokenTTL = $accessTokenTTL;
|
||||
$this->queryDelimiter = $queryDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateInterval $refreshTokenTTL
|
||||
*
|
||||
* @throw LogicException
|
||||
*/
|
||||
public function setRefreshTokenTTL(DateInterval $refreshTokenTTL)
|
||||
{
|
||||
throw new LogicException('The Implicit Grant does not return refresh tokens');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*
|
||||
* @throw LogicException
|
||||
*/
|
||||
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||
{
|
||||
throw new LogicException('The Implicit Grant does not return refresh tokens');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grant identifier that can be used in matching up requests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'implicit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to an incoming request.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
throw new LogicException('This grant does not used this method');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
return (
|
||||
isset($request->getQueryParams()['response_type'])
|
||||
&& $request->getQueryParams()['response_type'] === 'token'
|
||||
&& isset($request->getQueryParams()['client_id'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
$clientId = $this->getQueryStringParameter(
|
||||
'client_id',
|
||||
$request,
|
||||
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||
);
|
||||
|
||||
if (is_null($clientId)) {
|
||||
throw OAuthServerException::invalidRequest('client_id');
|
||||
}
|
||||
|
||||
$client = $this->getClientEntityOrFail($clientId, $request);
|
||||
|
||||
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$this->validateRedirectUri($redirectUri, $client, $request);
|
||||
} elseif (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|
||||
|| empty($client->getRedirectUri())) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient($request);
|
||||
} else {
|
||||
$redirectUri = is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri();
|
||||
}
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
|
||||
$redirectUri
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
|
||||
$authorizationRequest = new AuthorizationRequest();
|
||||
$authorizationRequest->setGrantTypeId($this->getIdentifier());
|
||||
$authorizationRequest->setClient($client);
|
||||
$authorizationRequest->setRedirectUri($redirectUri);
|
||||
|
||||
if ($stateParameter !== null) {
|
||||
$authorizationRequest->setState($stateParameter);
|
||||
}
|
||||
|
||||
$authorizationRequest->setScopes($scopes);
|
||||
|
||||
return $authorizationRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||
{
|
||||
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||
throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||
}
|
||||
|
||||
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
||||
? is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||
: $authorizationRequest->getClient()->getRedirectUri()
|
||||
: $authorizationRequest->getRedirectUri();
|
||||
|
||||
// The user approved the client, redirect them back with an access token
|
||||
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes(
|
||||
$authorizationRequest->getScopes(),
|
||||
$this->getIdentifier(),
|
||||
$authorizationRequest->getClient(),
|
||||
$authorizationRequest->getUser()->getIdentifier()
|
||||
);
|
||||
|
||||
$accessToken = $this->issueAccessToken(
|
||||
$this->accessTokenTTL,
|
||||
$authorizationRequest->getClient(),
|
||||
$authorizationRequest->getUser()->getIdentifier(),
|
||||
$finalizedScopes
|
||||
);
|
||||
|
||||
$response = new RedirectResponse();
|
||||
$response->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'access_token' => (string) $accessToken,
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - \time(),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
],
|
||||
$this->queryDelimiter
|
||||
)
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// The user denied the client, redirect them back with an error
|
||||
throw OAuthServerException::accessDenied(
|
||||
'The user denied the request',
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
[
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
120
src/Grant/PasswordGrant.php
Normal file
120
src/Grant/PasswordGrant.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Password grant.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Password grant class.
|
||||
*/
|
||||
class PasswordGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @param UserRepositoryInterface $userRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*/
|
||||
public function __construct(
|
||||
UserRepositoryInterface $userRepository,
|
||||
RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
) {
|
||||
$this->setUserRepository($userRepository);
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
|
||||
$this->refreshTokenTTL = new DateInterval('P1M');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
|
||||
$user = $this->validateUser($request, $client);
|
||||
|
||||
// Finalize the requested scopes
|
||||
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
|
||||
|
||||
// Issue and persist new access token
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||
$responseType->setAccessToken($accessToken);
|
||||
|
||||
// Issue and persist new refresh token if given
|
||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||
|
||||
if ($refreshToken !== null) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||
$responseType->setRefreshToken($refreshToken);
|
||||
}
|
||||
|
||||
return $responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ClientEntityInterface $client
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return UserEntityInterface
|
||||
*/
|
||||
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
|
||||
{
|
||||
$username = $this->getRequestParameter('username', $request);
|
||||
|
||||
if (is_null($username)) {
|
||||
throw OAuthServerException::invalidRequest('username');
|
||||
}
|
||||
|
||||
$password = $this->getRequestParameter('password', $request);
|
||||
|
||||
if (is_null($password)) {
|
||||
throw OAuthServerException::invalidRequest('password');
|
||||
}
|
||||
|
||||
$user = $this->userRepository->getUserEntityByUserCredentials(
|
||||
$username,
|
||||
$password,
|
||||
$this->getIdentifier(),
|
||||
$client
|
||||
);
|
||||
|
||||
if ($user instanceof UserEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
|
||||
|
||||
throw OAuthServerException::invalidGrant();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'password';
|
||||
}
|
||||
}
|
128
src/Grant/RefreshTokenGrant.php
Normal file
128
src/Grant/RefreshTokenGrant.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Refresh token grant.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use Exception;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestEvent;
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Refresh token grant.
|
||||
*/
|
||||
class RefreshTokenGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*/
|
||||
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||
{
|
||||
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||
|
||||
$this->refreshTokenTTL = new DateInterval('P1M');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
$oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier());
|
||||
$scopes = $this->validateScopes($this->getRequestParameter(
|
||||
'scope',
|
||||
$request,
|
||||
implode(self::SCOPE_DELIMITER_STRING, $oldRefreshToken['scopes']))
|
||||
);
|
||||
|
||||
// The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure
|
||||
// the request doesn't include any new scopes
|
||||
foreach ($scopes as $scope) {
|
||||
if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes'], true) === false) {
|
||||
throw OAuthServerException::invalidScope($scope->getIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
// Expire old tokens
|
||||
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
|
||||
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
|
||||
|
||||
// Issue and persist new access token
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||
$responseType->setAccessToken($accessToken);
|
||||
|
||||
// Issue and persist new refresh token if given
|
||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||
|
||||
if ($refreshToken !== null) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||
$responseType->setRefreshToken($refreshToken);
|
||||
}
|
||||
|
||||
return $responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string $clientId
|
||||
*
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function validateOldRefreshToken(ServerRequestInterface $request, $clientId)
|
||||
{
|
||||
$encryptedRefreshToken = $this->getRequestParameter('refresh_token', $request);
|
||||
if (is_null($encryptedRefreshToken)) {
|
||||
throw OAuthServerException::invalidRequest('refresh_token');
|
||||
}
|
||||
|
||||
// Validate refresh token
|
||||
try {
|
||||
$refreshToken = $this->decrypt($encryptedRefreshToken);
|
||||
} catch (Exception $e) {
|
||||
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token', $e);
|
||||
}
|
||||
|
||||
$refreshTokenData = json_decode($refreshToken, true);
|
||||
if ($refreshTokenData['client_id'] !== $clientId) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request));
|
||||
throw OAuthServerException::invalidRefreshToken('Token is not linked to client');
|
||||
}
|
||||
|
||||
if ($refreshTokenData['expire_time'] < time()) {
|
||||
throw OAuthServerException::invalidRefreshToken('Token has expired');
|
||||
}
|
||||
|
||||
if ($this->refreshTokenRepository->isRefreshTokenRevoked($refreshTokenData['refresh_token_id']) === true) {
|
||||
throw OAuthServerException::invalidRefreshToken('Token has been revoked');
|
||||
}
|
||||
|
||||
return $refreshTokenData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return 'refresh_token';
|
||||
}
|
||||
}
|
56
src/Middleware/AuthorizationServerMiddleware.php
Normal file
56
src/Middleware/AuthorizationServerMiddleware.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Middleware;
|
||||
|
||||
use Exception;
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class AuthorizationServerMiddleware
|
||||
{
|
||||
/**
|
||||
* @var AuthorizationServer
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* @param AuthorizationServer $server
|
||||
*/
|
||||
public function __construct(AuthorizationServer $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param callable $next
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||
{
|
||||
try {
|
||||
$response = $this->server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $exception) {
|
||||
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||
->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// Pass the request and response on to the next responder in the chain
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
56
src/Middleware/ResourceServerMiddleware.php
Normal file
56
src/Middleware/ResourceServerMiddleware.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Middleware;
|
||||
|
||||
use Exception;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\ResourceServer;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class ResourceServerMiddleware
|
||||
{
|
||||
/**
|
||||
* @var ResourceServer
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* @param ResourceServer $server
|
||||
*/
|
||||
public function __construct(ResourceServer $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param callable $next
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||
{
|
||||
try {
|
||||
$request = $this->server->validateAuthenticatedRequest($request);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $exception) {
|
||||
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||
->generateHttpResponse($response);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// Pass the request and response on to the next responder in the chain
|
||||
return $next($request, $response);
|
||||
}
|
||||
}
|
@@ -1,357 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Oauth2\Authentication;
|
||||
|
||||
interface Database
|
||||
{
|
||||
/**
|
||||
* Validate a client
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* # Client ID + redirect URI
|
||||
* SELECT clients.id FROM clients LEFT JOIN client_endpoints ON
|
||||
* client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND
|
||||
* client_endpoints.redirect_uri = $redirectUri
|
||||
*
|
||||
* # Client ID + client secret
|
||||
* SELECT clients.id FROM clients WHERE clients.id = $clientId AND
|
||||
* clients.secret = $clientSecret
|
||||
*
|
||||
* # Client ID + client secret + redirect URI
|
||||
* SELECT clients.id FROM clients LEFT JOIN client_endpoints ON
|
||||
* client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND
|
||||
* clients.secret = $clientSecret AND client_endpoints.redirect_uri =
|
||||
* $redirectUri
|
||||
* </code>
|
||||
*
|
||||
* Response:
|
||||
*
|
||||
* <code>
|
||||
* Array
|
||||
* (
|
||||
* [client_id] => (string) The client ID
|
||||
* [client secret] => (string) The client secret
|
||||
* [redirect_uri] => (string) The redirect URI used in this request
|
||||
* [name] => (string) The name of the client
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @param string $clientId The client's ID
|
||||
* @param string $clientSecret The client's secret (default = "null")
|
||||
* @param string $redirectUri The client's redirect URI (default = "null")
|
||||
* @return bool|array Returns false if the validation fails, array on success
|
||||
*/
|
||||
public function validateClient(
|
||||
$clientId,
|
||||
$clientSecret = null,
|
||||
$redirectUri = null
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a new OAuth session
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* INSERT INTO oauth_sessions (client_id, redirect_uri, owner_type,
|
||||
* owner_id, auth_code, access_token, refresh_token, stage, first_requested,
|
||||
* last_updated) VALUES ($clientId, $redirectUri, $type, $typeId, $authCode,
|
||||
* $accessToken, $stage, UNIX_TIMESTAMP(NOW()), UNIX_TIMESTAMP(NOW()))
|
||||
* </code>
|
||||
*
|
||||
* @param string $clientId The client ID
|
||||
* @param string $redirectUri The redirect URI
|
||||
* @param string $type The session owner's type (default = "user")
|
||||
* @param string $typeId The session owner's ID (default = "null")
|
||||
* @param string $authCode The authorisation code (default = "null")
|
||||
* @param string $accessToken The access token (default = "null")
|
||||
* @param string $refreshToken The refresh token (default = "null")
|
||||
* @param string $stage The stage of the session (default ="request")
|
||||
* @return int The session ID
|
||||
*/
|
||||
public function newSession(
|
||||
$clientId,
|
||||
$redirectUri,
|
||||
$type = 'user',
|
||||
$typeId = null,
|
||||
$authCode = null,
|
||||
$accessToken = null,
|
||||
$refreshToken = null,
|
||||
$accessTokenExpire = null,
|
||||
$stage = 'requested'
|
||||
);
|
||||
|
||||
/**
|
||||
* Update an OAuth session
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* UPDATE oauth_sessions SET auth_code = $authCode, access_token =
|
||||
* $accessToken, stage = $stage, last_updated = UNIX_TIMESTAMP(NOW()) WHERE
|
||||
* id = $sessionId
|
||||
* </code>
|
||||
*
|
||||
* @param string $sessionId The session ID
|
||||
* @param string $authCode The authorisation code (default = "null")
|
||||
* @param string $accessToken The access token (default = "null")
|
||||
* @param string $refreshToken The refresh token (default = "null")
|
||||
* @param string $stage The stage of the session (default ="request")
|
||||
* @return void
|
||||
*/
|
||||
public function updateSession(
|
||||
$sessionId,
|
||||
$authCode = null,
|
||||
$accessToken = null,
|
||||
$refreshToken = null,
|
||||
$accessTokenExpire = null,
|
||||
$stage = 'requested'
|
||||
);
|
||||
|
||||
/**
|
||||
* Delete an OAuth session
|
||||
*
|
||||
* <code>
|
||||
* DELETE FROM oauth_sessions WHERE client_id = $clientId AND owner_type =
|
||||
* $type AND owner_id = $typeId
|
||||
* </code>
|
||||
*
|
||||
* @param string $clientId The client ID
|
||||
* @param string $type The session owner's type
|
||||
* @param string $typeId The session owner's ID
|
||||
* @return void
|
||||
*/
|
||||
public function deleteSession(
|
||||
$clientId,
|
||||
$type,
|
||||
$typeId
|
||||
);
|
||||
|
||||
public function validateRefreshToken($refreshToken, $clientId);
|
||||
|
||||
/**
|
||||
* Update the refresh token
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* UPDATE oauth_sessions SET access_token = $newAccessToken, refresh_token =
|
||||
* $newRefreshToken, access_toke_expires = $accessTokenExpires, last_updated = UNIX_TIMESTAMP(NOW()) WHERE
|
||||
* id = $sessionId
|
||||
* </code>
|
||||
*
|
||||
* @param string $sessionId The session ID
|
||||
* @param string $newAccessToken The new access token for this session
|
||||
* @param string $newRefreshToken The new refresh token for the session
|
||||
* @param int $accessTokenExpires The UNIX timestamp of when the new token expires
|
||||
* @return void
|
||||
*/
|
||||
public function updateRefreshToken($sessionId, $newAccessToken, $newRefreshToken, $accessTokenExpires);
|
||||
|
||||
/**
|
||||
* Validate that an authorisation code is valid
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* SELECT id FROM oauth_sessions WHERE client_id = $clientID AND
|
||||
* redirect_uri = $redirectUri AND auth_code = $authCode
|
||||
* </code>
|
||||
*
|
||||
* Response:
|
||||
*
|
||||
* <code>
|
||||
* Array
|
||||
* (
|
||||
* [id] => (int) The session ID
|
||||
* [client_id] => (string) The client ID
|
||||
* [redirect_uri] => (string) The redirect URI
|
||||
* [owner_type] => (string) The session owner type
|
||||
* [owner_id] => (string) The session owner's ID
|
||||
* [auth_code] => (string) The authorisation code
|
||||
* [stage] => (string) The session's stage
|
||||
* [first_requested] => (int) Unix timestamp of the time the session was
|
||||
* first generated
|
||||
* [last_updated] => (int) Unix timestamp of the time the session was
|
||||
* last updated
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @param string $clientId The client ID
|
||||
* @param string $redirectUri The redirect URI
|
||||
* @param string $authCode The authorisation code
|
||||
* @return int|bool Returns the session ID if the auth code
|
||||
* is valid otherwise returns false
|
||||
*/
|
||||
public function validateAuthCode(
|
||||
$clientId,
|
||||
$redirectUri,
|
||||
$authCode
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the session ID for a given session owner and client combination
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* SELECT id FROM oauth_sessions WHERE client_id = $clientId
|
||||
* AND owner_type = $type AND owner_id = $typeId
|
||||
* </code>
|
||||
*
|
||||
* @param string $type The session owner's type
|
||||
* @param string $typeId The session owner's ID
|
||||
* @param string $clientId The client ID
|
||||
* @return string|null Return the session ID as an integer if
|
||||
* found otherwise returns false
|
||||
*/
|
||||
public function hasSession(
|
||||
$type,
|
||||
$typeId,
|
||||
$clientId
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the access token for a given session
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* SELECT access_token FROM oauth_sessions WHERE id = $sessionId
|
||||
* </code>
|
||||
*
|
||||
* @param int $sessionId The OAuth session ID
|
||||
* @return string|null Returns the access token as a string if
|
||||
* found otherwise returns null
|
||||
*/
|
||||
public function getAccessToken($sessionId);
|
||||
|
||||
/**
|
||||
* Removes an authorisation code associated with a session
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* UPDATE oauth_sessions SET auth_code = NULL WHERE id = $sessionId
|
||||
* </code>
|
||||
*
|
||||
* @param int $sessionId The OAuth session ID
|
||||
* @return void
|
||||
*/
|
||||
public function removeAuthCode($sessionId);
|
||||
|
||||
/**
|
||||
* Sets a sessions access token
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* UPDATE oauth_sessions SET access_token = $accessToken WHERE id =
|
||||
* $sessionId
|
||||
* </code>
|
||||
*
|
||||
* @param int $sessionId The OAuth session ID
|
||||
* @param string $accessToken The access token
|
||||
* @return void
|
||||
*/
|
||||
public function setAccessToken(
|
||||
$sessionId,
|
||||
$accessToken
|
||||
);
|
||||
|
||||
/**
|
||||
* Associates a session with a scope
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* INSERT INTO oauth_session_scopes (session_id, scope) VALUE ($sessionId,
|
||||
* $scope)
|
||||
* </code>
|
||||
*
|
||||
* @param int $sessionId The session ID
|
||||
* @param string $scope The scope
|
||||
* @return void
|
||||
*/
|
||||
public function addSessionScope(
|
||||
$sessionId,
|
||||
$scope
|
||||
);
|
||||
|
||||
/**
|
||||
* Return information about a scope
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* SELECT * FROM scopes WHERE scope = $scope
|
||||
* </code>
|
||||
*
|
||||
* Response:
|
||||
*
|
||||
* <code>
|
||||
* Array
|
||||
* (
|
||||
* [id] => (int) The scope's ID
|
||||
* [scope] => (string) The scope itself
|
||||
* [name] => (string) The scope's name
|
||||
* [description] => (string) The scope's description
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @param string $scope The scope
|
||||
* @return array
|
||||
*/
|
||||
public function getScope($scope);
|
||||
|
||||
/**
|
||||
* Associate a session's scopes with an access token
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* UPDATE oauth_session_scopes SET access_token = $accessToken WHERE
|
||||
* session_id = $sessionId
|
||||
* </code>
|
||||
*
|
||||
* @param int $sessionId The session ID
|
||||
* @param string $accessToken The access token
|
||||
* @return void
|
||||
*/
|
||||
public function updateSessionScopeAccessToken(
|
||||
$sessionId,
|
||||
$accessToken
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the scopes associated with an access token
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* SELECT scopes.scope, scopes.name, scopes.description FROM
|
||||
* oauth_session_scopes JOIN scopes ON oauth_session_scopes.scope =
|
||||
* scopes.scope WHERE access_token = $accessToken
|
||||
* </code>
|
||||
*
|
||||
* Response:
|
||||
*
|
||||
* <code>
|
||||
* Array
|
||||
* (
|
||||
* [0] => Array
|
||||
* (
|
||||
* [scope] => (string) The scope
|
||||
* [name] => (string) The scope's name
|
||||
* [description] => (string) The scope's description
|
||||
* )
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @param string $accessToken The access token
|
||||
* @return array
|
||||
*/
|
||||
public function accessTokenScopes($accessToken);
|
||||
}
|
@@ -1,791 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Oauth2\Authentication;
|
||||
|
||||
class ClientException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class UserException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class ServerException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* Reference to the database abstractor
|
||||
* @var object
|
||||
*/
|
||||
private $_db = null;
|
||||
|
||||
/**
|
||||
* Server configuration
|
||||
* @var array
|
||||
*/
|
||||
private $_config = array(
|
||||
'scope_delimeter' => ',',
|
||||
'access_token_ttl' => 3600
|
||||
);
|
||||
|
||||
/**
|
||||
* Supported response types
|
||||
* @var array
|
||||
*/
|
||||
private $_responseTypes = array(
|
||||
'code'
|
||||
);
|
||||
|
||||
/**
|
||||
* Supported grant types
|
||||
* @var array
|
||||
*/
|
||||
private $_grantTypes = array(
|
||||
'authorization_code' => false,
|
||||
'client_credentials' => false,
|
||||
'password' => false,
|
||||
'refresh_token' => false,
|
||||
);
|
||||
|
||||
private $_grantTypeCallbacks = array(
|
||||
'password' => null
|
||||
);
|
||||
|
||||
/**
|
||||
* Exception error codes
|
||||
* @var array
|
||||
*/
|
||||
public $exceptionCodes = array(
|
||||
0 => 'invalid_request',
|
||||
1 => 'unauthorized_client',
|
||||
2 => 'access_denied',
|
||||
3 => 'unsupported_response_type',
|
||||
4 => 'invalid_scope',
|
||||
5 => 'server_error',
|
||||
6 => 'temporarily_unavailable',
|
||||
7 => 'unsupported_grant_type',
|
||||
8 => 'invalid_client',
|
||||
9 => 'invalid_grant'
|
||||
);
|
||||
|
||||
/**
|
||||
* Error codes.
|
||||
*
|
||||
* To provide i8ln errors just overwrite the keys
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $errors = array(
|
||||
'invalid_request' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.',
|
||||
'unauthorized_client' => 'The client is not authorized to request an access token using this method.',
|
||||
'access_denied' => 'The resource owner or authorization server denied the request.',
|
||||
'unsupported_response_type' => 'The authorization server does not support obtaining an access token using this method.',
|
||||
'invalid_scope' => 'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.',
|
||||
'server_error' => 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.',
|
||||
'temporarily_unavailable' => 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.',
|
||||
'unsupported_grant_type' => 'The authorization grant type "%s" is not supported by the authorization server',
|
||||
'invalid_client' => 'Client authentication failed',
|
||||
'invalid_grant' => 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Check the "%s" parameter.',
|
||||
'invalid_credentials' => 'The user credentials were incorrect.',
|
||||
'invalid_refresh' => 'The refresh token is invalid.',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param array $options Optional list of options to overwrite the defaults
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($options = null)
|
||||
{
|
||||
if ($options !== null) {
|
||||
$this->_config = array_merge($this->_config, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a database abstrator class
|
||||
*
|
||||
* @access public
|
||||
* @param object $db A class that implements OAuth2ServerDatabase
|
||||
* @return void
|
||||
*/
|
||||
public function registerDbAbstractor($db)
|
||||
{
|
||||
$this->_db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a grant type
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function enableGrantType($type, $callback = null)
|
||||
{
|
||||
if (isset($this->_grantTypes[$type])) {
|
||||
$this->_grantTypes[$type] = true;
|
||||
}
|
||||
|
||||
if (in_array($type, array_keys($this->_grantTypeCallbacks))) {
|
||||
if (is_null($callback) || ! is_callable($callback)) {
|
||||
throw new ServerException('No registered callback function for grant type `'.$type.'`');
|
||||
}
|
||||
|
||||
$this->_grantTypeCallbacks[$type] = $callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check client authorise parameters
|
||||
*
|
||||
* @access public
|
||||
* @param array $authParams Optional array of parsed $_GET keys
|
||||
* @return array Authorise request parameters
|
||||
*/
|
||||
public function checkClientAuthoriseParams($authParams = null)
|
||||
{
|
||||
$params = array();
|
||||
|
||||
// Client ID
|
||||
if ( ! isset($authParams['client_id']) && ! isset($_GET['client_id'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
|
||||
}
|
||||
|
||||
$params['client_id'] = (isset($authParams['client_id'])) ?
|
||||
$authParams['client_id'] :
|
||||
$_GET['client_id'];
|
||||
|
||||
// Redirect URI
|
||||
if ( ! isset($authParams['redirect_uri']) && ! isset($_GET['redirect_uri'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0);
|
||||
}
|
||||
|
||||
$params['redirect_uri'] = (isset($authParams['redirect_uri'])) ?
|
||||
$authParams['redirect_uri'] :
|
||||
$_GET['redirect_uri'];
|
||||
|
||||
// Validate client ID and redirect URI
|
||||
$clientDetails = $this->_dbCall(
|
||||
'validateClient',
|
||||
$params['client_id'],
|
||||
null,
|
||||
$params['redirect_uri']
|
||||
);
|
||||
|
||||
if ($clientDetails === false) {
|
||||
throw new ClientException($this->errors['invalid_client'], 8);
|
||||
}
|
||||
|
||||
$params['client_details'] = $clientDetails;
|
||||
|
||||
// Response type
|
||||
if ( ! isset($authParams['response_type']) && ! isset($_GET['response_type'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'response_type'), 0);
|
||||
}
|
||||
|
||||
$params['response_type'] = (isset($authParams['response_type'])) ?
|
||||
$authParams['response_type'] :
|
||||
$_GET['response_type'];
|
||||
|
||||
// Ensure response type is one that is recognised
|
||||
if ( ! in_array($params['response_type'], $this->_responseTypes)) {
|
||||
throw new ClientException($this->errors['unsupported_response_type'], 3);
|
||||
}
|
||||
|
||||
// Get and validate scopes
|
||||
if (isset($authParams['scope']) || isset($_GET['scope'])) {
|
||||
|
||||
$scopes = (isset($_GET['scope'])) ?
|
||||
$_GET['scope'] :
|
||||
$authParams['scope'];
|
||||
|
||||
$scopes = explode($this->_config['scope_delimeter'], $scopes);
|
||||
|
||||
// Remove any junk scopes
|
||||
for ($i = 0; $i < count($scopes); $i++) {
|
||||
|
||||
$scopes[$i] = trim($scopes[$i]);
|
||||
|
||||
if ($scopes[$i] === '') {
|
||||
unset($scopes[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($scopes) === 0) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'scope'), 0);
|
||||
}
|
||||
|
||||
$params['scopes'] = array();
|
||||
|
||||
foreach ($scopes as $scope) {
|
||||
|
||||
$scopeDetails = $this->_dbCall(
|
||||
'getScope',
|
||||
$scope
|
||||
);
|
||||
|
||||
if ($scopeDetails === false) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_scope'], $scope), 4);
|
||||
}
|
||||
|
||||
$params['scopes'][] = $scopeDetails;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a new authorise request
|
||||
*
|
||||
* @param string $type The session owner's type
|
||||
* @param string $typeId The session owner's ID
|
||||
* @param array $authoriseParams The authorise request $_GET parameters
|
||||
* @return string An authorisation code
|
||||
*/
|
||||
public function newAuthoriseRequest($type, $typeId, $authoriseParams)
|
||||
{
|
||||
// Remove any old sessions the user might have
|
||||
$this->_dbCall(
|
||||
'deleteSession',
|
||||
$authoriseParams['client_id'],
|
||||
$type,
|
||||
$typeId
|
||||
);
|
||||
|
||||
// Create the new auth code
|
||||
$authCode = $this->_newAuthCode(
|
||||
$authoriseParams['client_id'],
|
||||
'user',
|
||||
$typeId,
|
||||
$authoriseParams['redirect_uri'],
|
||||
$authoriseParams['scopes']
|
||||
);
|
||||
|
||||
return $authCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique code
|
||||
*
|
||||
* Generate a unique code for an authorisation code, or token
|
||||
*
|
||||
* @return string A unique code
|
||||
*/
|
||||
private function _generateCode()
|
||||
{
|
||||
return sha1(uniqid(microtime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new authorisation code
|
||||
*
|
||||
* @param string $clientId The client ID
|
||||
* @param string $type The type of the owner of the session
|
||||
* @param string $typeId The session owner's ID
|
||||
* @param string $redirectUri The redirect URI
|
||||
* @param array $scopes The requested scopes
|
||||
* @param string $accessToken The access token (default = null)
|
||||
* @return string An authorisation code
|
||||
*/
|
||||
private function _newAuthCode($clientId, $type, $typeId, $redirectUri, $scopes = array())
|
||||
{
|
||||
$authCode = $this->_generateCode();
|
||||
|
||||
// Delete any existing sessions just to be sure
|
||||
$this->_dbCall('deleteSession', $clientId, $type, $typeId);
|
||||
|
||||
// Create a new session
|
||||
$sessionId = $this->_dbCall(
|
||||
'newSession',
|
||||
$clientId,
|
||||
$redirectUri,
|
||||
$type,
|
||||
$typeId,
|
||||
$authCode,
|
||||
null,
|
||||
null,
|
||||
'requested'
|
||||
);
|
||||
|
||||
// Add the scopes
|
||||
foreach ($scopes as $key => $scope) {
|
||||
|
||||
$this->_dbCall(
|
||||
'addSessionScope',
|
||||
$sessionId,
|
||||
$scope['scope']
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return $authCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an access token
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param array $authParams Optional array of parsed $_POST keys
|
||||
*
|
||||
* @return array Authorise request parameters
|
||||
*/
|
||||
public function issueAccessToken($authParams = null)
|
||||
{
|
||||
if ( ! isset($authParams['grant_type']) && ! isset($_POST['grant_type'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'grant_type'), 0);
|
||||
}
|
||||
|
||||
$params['grant_type'] = (isset($authParams['grant_type'])) ?
|
||||
$authParams['grant_type'] :
|
||||
$_POST['grant_type'];
|
||||
|
||||
// Ensure grant type is one that is recognised and is enabled
|
||||
if ( ! in_array($params['grant_type'], array_keys($this->_grantTypes)) || $this->_grantTypes[$params['grant_type']] !== true) {
|
||||
throw new ClientException(sprintf($this->errors['unsupported_grant_type'], $params['grant_type']), 7);
|
||||
}
|
||||
|
||||
switch ($params['grant_type'])
|
||||
{
|
||||
case 'authorization_code': // Authorization code grant
|
||||
return $this->_completeAuthCodeGrant($authParams, $params);
|
||||
break;
|
||||
|
||||
case 'client_credentials': // Client credentials grant
|
||||
return $this->_completeClientCredentialsGrant($authParams, $params);
|
||||
break;
|
||||
|
||||
case 'password': // Resource owner password credentials grant
|
||||
return $this->_completeUserCredentialsGrant($authParams, $params);
|
||||
break;
|
||||
|
||||
case 'refresh_token': // Refresh token grant
|
||||
return $this->_completeRefreshTokenGrant($authParams, $params);
|
||||
break;
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
default: // Unsupported
|
||||
throw new ServerException($this->errors['server_error'] . 'Tried to process an unsuppported grant type.', 5);
|
||||
break;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the authorisation code grant
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param array $authParams Array of parsed $_POST keys
|
||||
* @param array $params Generated parameters from issueAccessToken()
|
||||
*
|
||||
* @return array Authorise request parameters
|
||||
*/
|
||||
private function _completeAuthCodeGrant($authParams = array(), $params = array())
|
||||
{
|
||||
// Client ID
|
||||
if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
|
||||
}
|
||||
|
||||
$params['client_id'] = (isset($authParams['client_id'])) ?
|
||||
$authParams['client_id'] :
|
||||
$_POST['client_id'];
|
||||
|
||||
// Client secret
|
||||
if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0);
|
||||
}
|
||||
|
||||
$params['client_secret'] = (isset($authParams['client_secret'])) ?
|
||||
$authParams['client_secret'] :
|
||||
$_POST['client_secret'];
|
||||
|
||||
// Redirect URI
|
||||
if ( ! isset($authParams['redirect_uri']) && ! isset($_POST['redirect_uri'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0);
|
||||
}
|
||||
|
||||
$params['redirect_uri'] = (isset($authParams['redirect_uri'])) ?
|
||||
$authParams['redirect_uri'] :
|
||||
$_POST['redirect_uri'];
|
||||
|
||||
// Validate client ID and redirect URI
|
||||
$clientDetails = $this->_dbCall(
|
||||
'validateClient',
|
||||
$params['client_id'],
|
||||
$params['client_secret'],
|
||||
$params['redirect_uri']
|
||||
);
|
||||
|
||||
if ($clientDetails === false) {
|
||||
throw new ClientException($this->errors['invalid_client'], 8);
|
||||
}
|
||||
|
||||
// The authorization code
|
||||
if ( ! isset($authParams['code']) && ! isset($_POST['code'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'code'), 0);
|
||||
}
|
||||
|
||||
$params['code'] = (isset($authParams['code'])) ?
|
||||
$authParams['code'] :
|
||||
$_POST['code'];
|
||||
|
||||
// Verify the authorization code matches the client_id and the request_uri
|
||||
$session = $this->_dbCall(
|
||||
'validateAuthCode',
|
||||
$params['client_id'],
|
||||
$params['redirect_uri'],
|
||||
$params['code']
|
||||
);
|
||||
|
||||
if ( ! $session) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_grant'], 'code'), 9);
|
||||
}
|
||||
|
||||
// A session ID was returned so update it with an access token,
|
||||
// remove the authorisation code, change the stage to 'granted'
|
||||
|
||||
$accessToken = $this->_generateCode();
|
||||
$refreshToken = ($this->_grantTypes['refresh_token']) ?
|
||||
$this->_generateCode() :
|
||||
null;
|
||||
|
||||
$accessTokenExpires = time() + $this->_config['access_token_ttl'];
|
||||
$accessTokenExpiresIn = $this->_config['access_token_ttl'];
|
||||
|
||||
$this->_dbCall(
|
||||
'updateSession',
|
||||
$session['id'],
|
||||
null,
|
||||
$accessToken,
|
||||
$refreshToken,
|
||||
$accessTokenExpires,
|
||||
'granted'
|
||||
);
|
||||
|
||||
// Update the session's scopes to reference the access token
|
||||
$this->_dbCall(
|
||||
'updateSessionScopeAccessToken',
|
||||
$session['id'],
|
||||
$accessToken,
|
||||
$refreshToken
|
||||
);
|
||||
|
||||
$response = array(
|
||||
'access_token' => $accessToken,
|
||||
'token_type' => 'bearer',
|
||||
'expires' => $accessTokenExpires,
|
||||
'expires_in' => $accessTokenExpiresIn
|
||||
);
|
||||
|
||||
if ($this->_grantTypes['refresh_token']) {
|
||||
$response['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the resource owner password credentials grant
|
||||
*
|
||||
* @access private
|
||||
* @param array $authParams Array of parsed $_POST keys
|
||||
* @param array $params Generated parameters from issueAccessToken()
|
||||
* @return array Authorise request parameters
|
||||
*/
|
||||
private function _completeClientCredentialsGrant($authParams = array(), $params = array())
|
||||
{
|
||||
// Client ID
|
||||
if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$params['client_id'] = (isset($authParams['client_id'])) ?
|
||||
$authParams['client_id'] :
|
||||
$_POST['client_id'];
|
||||
|
||||
// Client secret
|
||||
if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$params['client_secret'] = (isset($authParams['client_secret'])) ?
|
||||
$authParams['client_secret'] :
|
||||
$_POST['client_secret'];
|
||||
|
||||
// Validate client ID and client secret
|
||||
$clientDetails = $this->_dbCall(
|
||||
'validateClient',
|
||||
$params['client_id'],
|
||||
$params['client_secret'],
|
||||
null
|
||||
);
|
||||
|
||||
if ($clientDetails === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException($this->errors['invalid_client'], 8);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// Generate an access token
|
||||
$accessToken = $this->_generateCode();
|
||||
$refreshToken = ($this->_grantTypes['refresh_token']) ?
|
||||
$this->_generateCode() :
|
||||
null;
|
||||
|
||||
$accessTokenExpires = time() + $this->_config['access_token_ttl'];
|
||||
$accessTokenExpiresIn = $this->_config['access_token_ttl'];
|
||||
|
||||
// Delete any existing sessions just to be sure
|
||||
$this->_dbCall('deleteSession', $params['client_id'], 'client', $params['client_id']);
|
||||
|
||||
// Create a new session
|
||||
$this->_dbCall('newSession', $params['client_id'], null, 'client', $params['client_id'], null, $accessToken, $refreshToken, $accessTokenExpires, 'granted');
|
||||
|
||||
$response = array(
|
||||
'access_token' => $accessToken,
|
||||
'token_type' => 'bearer',
|
||||
'expires' => $accessTokenExpires,
|
||||
'expires_in' => $accessTokenExpiresIn
|
||||
);
|
||||
|
||||
if ($this->_grantTypes['refresh_token']) {
|
||||
$response['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the resource owner password credentials grant
|
||||
*
|
||||
* @access private
|
||||
* @param array $authParams Array of parsed $_POST keys
|
||||
* @param array $params Generated parameters from issueAccessToken()
|
||||
* @return array Authorise request parameters
|
||||
*/
|
||||
private function _completeUserCredentialsGrant($authParams = array(), $params = array())
|
||||
{
|
||||
// Client ID
|
||||
if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$params['client_id'] = (isset($authParams['client_id'])) ?
|
||||
$authParams['client_id'] :
|
||||
$_POST['client_id'];
|
||||
|
||||
// Client secret
|
||||
if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$params['client_secret'] = (isset($authParams['client_secret'])) ?
|
||||
$authParams['client_secret'] :
|
||||
$_POST['client_secret'];
|
||||
|
||||
// Validate client ID and client secret
|
||||
$clientDetails = $this->_dbCall(
|
||||
'validateClient',
|
||||
$params['client_id'],
|
||||
$params['client_secret'],
|
||||
null
|
||||
);
|
||||
|
||||
if ($clientDetails === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException($this->errors['invalid_client'], 8);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// User's username
|
||||
if ( ! isset($authParams['username']) && ! isset($_POST['username'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'username'), 0);
|
||||
}
|
||||
|
||||
$params['username'] = (isset($authParams['username'])) ?
|
||||
$authParams['username'] :
|
||||
$_POST['username'];
|
||||
|
||||
// User's password
|
||||
if ( ! isset($authParams['password']) && ! isset($_POST['password'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'password'), 0);
|
||||
}
|
||||
|
||||
$params['password'] = (isset($authParams['password'])) ?
|
||||
$authParams['password'] :
|
||||
$_POST['password'];
|
||||
|
||||
// Check if user's username and password are correct
|
||||
$userId = call_user_func($this->_grantTypeCallbacks['password'], $params['username'], $params['password']);
|
||||
|
||||
if ($userId === false) {
|
||||
throw new \Oauth2\Authentication\ClientException($this->errors['invalid_credentials'], 0);
|
||||
}
|
||||
|
||||
// Generate an access token
|
||||
$accessToken = $this->_generateCode();
|
||||
$refreshToken = ($this->_grantTypes['refresh_token']) ?
|
||||
$this->_generateCode() :
|
||||
null;
|
||||
|
||||
$accessTokenExpires = time() + $this->_config['access_token_ttl'];
|
||||
$accessTokenExpiresIn = $this->_config['access_token_ttl'];
|
||||
|
||||
// Delete any existing sessions just to be sure
|
||||
$this->_dbCall('deleteSession', $params['client_id'], 'user', $userId);
|
||||
|
||||
// Create a new session
|
||||
$this->_dbCall('newSession', $params['client_id'], null, 'user', $userId, null, $accessToken, $refreshToken, $accessTokenExpires, 'granted');
|
||||
|
||||
$response = array(
|
||||
'access_token' => $accessToken,
|
||||
'token_type' => 'bearer',
|
||||
'expires' => $accessTokenExpires,
|
||||
'expires_in' => $accessTokenExpiresIn
|
||||
);
|
||||
|
||||
if ($this->_grantTypes['refresh_token']) {
|
||||
$response['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the refresh token grant
|
||||
*
|
||||
* @access private
|
||||
* @param array $authParams Array of parsed $_POST keys
|
||||
* @param array $params Generated parameters from issueAccessToken()
|
||||
* @return array Authorise request parameters
|
||||
*/
|
||||
private function _completeRefreshTokenGrant($authParams = array(), $params = array())
|
||||
{
|
||||
// Client ID
|
||||
if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$params['client_id'] = (isset($authParams['client_id'])) ?
|
||||
$authParams['client_id'] :
|
||||
$_POST['client_id'];
|
||||
|
||||
// Client secret
|
||||
if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$params['client_secret'] = (isset($authParams['client_secret'])) ?
|
||||
$authParams['client_secret'] :
|
||||
$_POST['client_secret'];
|
||||
|
||||
// Validate client ID and client secret
|
||||
$clientDetails = $this->_dbCall(
|
||||
'validateClient',
|
||||
$params['client_id'],
|
||||
$params['client_secret'],
|
||||
null
|
||||
);
|
||||
|
||||
if ($clientDetails === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new ClientException($this->errors['invalid_client'], 8);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
// Refresh token
|
||||
if ( ! isset($authParams['refresh_token']) && ! isset($_POST['refresh_token'])) {
|
||||
throw new ClientException(sprintf($this->errors['invalid_request'], 'refresh_token'), 0);
|
||||
}
|
||||
|
||||
$params['refresh_token'] = (isset($authParams['refresh_token'])) ?
|
||||
$authParams['refresh_token'] :
|
||||
$_POST['refresh_token'];
|
||||
|
||||
// Validate refresh token
|
||||
$sessionId = $this->_dbCall('validateRefreshToken', $params['refresh_token'], $params['client_id']);
|
||||
|
||||
if ($sessionId === false) {
|
||||
throw new \Oauth2\Authentication\ClientException($this->errors['invalid_refresh'], 0);
|
||||
}
|
||||
|
||||
// Generate new tokens
|
||||
$accessToken = $this->_generateCode();
|
||||
$refreshToken = $this->_generateCode();
|
||||
|
||||
$accessTokenExpires = time() + $this->_config['access_token_ttl'];
|
||||
$accessTokenExpiresIn = $this->_config['access_token_ttl'];
|
||||
|
||||
// Update the tokens
|
||||
$this->_dbCall('updateRefreshToken', $sessionId, $accessToken, $refreshToken, $accessTokenExpires);
|
||||
|
||||
return array(
|
||||
'access_token' => $accessToken,
|
||||
'refresh_token' => $refreshToken,
|
||||
'token_type' => 'bearer',
|
||||
'expires' => $accessTokenExpires,
|
||||
'expires_in' => $accessTokenExpiresIn
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the redirect uri with appended params
|
||||
*
|
||||
* @param string $redirectUri The redirect URI
|
||||
* @param array $params The parameters to be appended to the URL
|
||||
* @param string $query_delimeter The query string delimiter (default: ?)
|
||||
*
|
||||
* @return string The updated redirect URI
|
||||
*/
|
||||
public function redirectUri($redirectUri, $params = array(), $queryDelimeter = '?')
|
||||
{
|
||||
return (strstr($redirectUri, $queryDelimeter)) ? $redirectUri . '&' . http_build_query($params) : $redirectUri . $queryDelimeter . http_build_query($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call database methods from the abstractor
|
||||
*
|
||||
* @return mixed The query result
|
||||
*/
|
||||
private function _dbCall()
|
||||
{
|
||||
if ($this->_db === null) {
|
||||
throw new ServerException('No registered database abstractor');
|
||||
}
|
||||
|
||||
if ( ! $this->_db instanceof Database) {
|
||||
throw new ServerException('Registered database abstractor is not an instance of Oauth2\Authentication\Database');
|
||||
}
|
||||
|
||||
$args = func_get_args();
|
||||
$method = $args[0];
|
||||
unset($args[0]);
|
||||
$params = array_values($args);
|
||||
|
||||
return call_user_func_array(array($this->_db, $method), $params);
|
||||
}
|
||||
}
|
@@ -1,230 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OAuth2\Client;
|
||||
|
||||
use Guzzle\Service\Client as GuzzleClient;
|
||||
|
||||
class IDPException extends \Exception
|
||||
{
|
||||
protected $result;
|
||||
|
||||
public function __construct($result)
|
||||
{
|
||||
$this->result = $result;
|
||||
|
||||
$code = isset($result['code']) ? $result['code'] : 0;
|
||||
|
||||
if (isset($result['error'])) {
|
||||
|
||||
// OAuth 2.0 Draft 10 style
|
||||
$message = $result['error'];
|
||||
|
||||
} elseif (isset($result['message'])) {
|
||||
|
||||
// cURL style
|
||||
$message = $result['message'];
|
||||
|
||||
} else {
|
||||
|
||||
$message = 'Unknown Error.';
|
||||
|
||||
}
|
||||
|
||||
parent::__construct($message['message'], $message['code']);
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
if (isset($this->result['error'])) {
|
||||
|
||||
$message = $this->result['error'];
|
||||
|
||||
if (is_string($message)) {
|
||||
// OAuth 2.0 Draft 10 style
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
||||
return 'Exception';
|
||||
}
|
||||
|
||||
/**
|
||||
* To make debugging easier.
|
||||
*
|
||||
* @returns
|
||||
* The string representation of the error.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$str = $this->getType() . ': ';
|
||||
|
||||
if ($this->code != 0) {
|
||||
$str .= $this->code . ': ';
|
||||
}
|
||||
|
||||
return $str . $this->message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class IDP {
|
||||
|
||||
public $clientId = '';
|
||||
|
||||
public $clientSecret = '';
|
||||
|
||||
public $redirectUri = '';
|
||||
|
||||
public $name;
|
||||
|
||||
public $uidKey = 'uid';
|
||||
|
||||
public $scopes = array();
|
||||
|
||||
public $method = 'post';
|
||||
|
||||
public $scopeSeperator = ',';
|
||||
|
||||
public $responseType = 'json';
|
||||
|
||||
public function __construct($options)
|
||||
{
|
||||
foreach ($options as $option => $value) {
|
||||
if (isset($this->{$option})) {
|
||||
$this->{$option} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function urlAuthorize();
|
||||
|
||||
abstract public function urlAccessToken();
|
||||
|
||||
abstract public function urlUserDetails(\Oauth2\Client\Token\Access $token);
|
||||
|
||||
abstract public function userDetails($response, \Oauth2\Client\Token\Access $token);
|
||||
|
||||
public function authorize($options = array())
|
||||
{
|
||||
$state = md5(uniqid(rand(), TRUE));
|
||||
setcookie($this->name.'_authorize_state', $state);
|
||||
|
||||
$params = array(
|
||||
'client_id' => $this->clientId,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'state' => $state,
|
||||
'scope' => is_array($this->scope) ? implode($this->scopeSeperator, $this->scope) : $this->scope,
|
||||
'response_type' => isset($options['response_type']) ? $options['response_type'] : 'code',
|
||||
'approval_prompt' => 'force' // - google force-recheck
|
||||
);
|
||||
|
||||
header('Location: ' . $this->urlAuthorize().'?'.http_build_query($params));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function getAccessToken($code = NULL, $options = array())
|
||||
{
|
||||
if ($code === NULL) {
|
||||
throw new \BadMethodCallException('Missing authorization code');
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'grant_type' => isset($options['grantType']) ? $options['grantType'] : 'authorization_code',
|
||||
);
|
||||
|
||||
switch ($params['grant_type']) {
|
||||
|
||||
case 'authorization_code':
|
||||
$params['code'] = $code;
|
||||
$params['redirect_uri'] = isset($options['redirectUri']) ? $options['redirectUri'] : $this->redirectUri;
|
||||
break;
|
||||
|
||||
case 'refresh_token':
|
||||
$params['refresh_token'] = $code;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
switch ($this->method) {
|
||||
|
||||
case 'get':
|
||||
|
||||
$client = new GuzzleClient($this->urlAccessToken() . '?' . http_build_query($params));
|
||||
$request = $client->send();
|
||||
$response = $request->getBody();
|
||||
|
||||
break;
|
||||
|
||||
case 'post':
|
||||
|
||||
$client = new GuzzleClient($this->urlAccessToken());
|
||||
$request = $client->post(null, null, $params)->send();
|
||||
$response = $request->getBody();
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
catch (\Guzzle\Http\Exception\BadResponseException $e)
|
||||
{
|
||||
$raw_response = explode("\n", $e->getResponse());
|
||||
$response = end($raw_response);
|
||||
}
|
||||
|
||||
switch ($this->responseType) {
|
||||
|
||||
case 'json':
|
||||
$result = json_decode($response, true);
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
parse_str($response, $result);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (isset($result['error']) && ! empty($result['error'])) {
|
||||
|
||||
throw new \Oauth2\Client\IDPException($result);
|
||||
|
||||
}
|
||||
|
||||
switch ($params['grant_type']) {
|
||||
|
||||
case 'authorization_code':
|
||||
return \Oauth2\Client\Token::factory('access', $result);
|
||||
break;
|
||||
|
||||
case 'refresh_token':
|
||||
return \Oauth2\Client\Token::factory('refresh', $result);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserDetails(\Oauth2\Client\Token\Access $token)
|
||||
{
|
||||
$url = $this->urlUserDetails($token);
|
||||
|
||||
try {
|
||||
$client = new GuzzleClient($url);
|
||||
$request = $client->get()->send();
|
||||
$response = $request->getBody();
|
||||
|
||||
return $this->userDetails(json_decode($response), $token);
|
||||
}
|
||||
|
||||
catch (\Guzzle\Http\Exception\BadResponseException $e)
|
||||
{
|
||||
$raw_response = explode("\n", $e->getResponse());
|
||||
throw new \Oauth2\Client\IDPException(end($raw_response));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OAuth2\Client;
|
||||
|
||||
class Provider
|
||||
{
|
||||
private function __constuct() {}
|
||||
|
||||
public static function factory($name, array $options = null)
|
||||
{
|
||||
if ( ! class_exists($name)) {
|
||||
|
||||
throw new OAuth2\Client\Exception('There is no identity provider called: '.$name);
|
||||
|
||||
}
|
||||
|
||||
return new $name($options);
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
class Blooie extends Oauth2\Client\IDP
|
||||
{
|
||||
public $scope = array('user.profile', 'user.picture');
|
||||
|
||||
public $method = 'POST';
|
||||
|
||||
public function urlAuthorize()
|
||||
{
|
||||
return 'https://bloo.ie/oauth';
|
||||
}
|
||||
|
||||
public function urlAccessToken()
|
||||
{
|
||||
return 'https://bloo.ie/oauth/access_token';
|
||||
}
|
||||
|
||||
public function getUserInfo(Oauth2\Token\Access $token)
|
||||
{
|
||||
$url = 'https://graph.facebook.com/me?'.http_build_query(array(
|
||||
'access_token' => $token->access_token,
|
||||
));
|
||||
|
||||
$user = json_decode(file_get_contents($url));
|
||||
|
||||
return array(
|
||||
'uid' => $user->id,
|
||||
'nickname' => $user->username,
|
||||
'name' => $user->name,
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'email' => isset($user->email) ? $user->email : null,
|
||||
'location' => isset($user->hometown->name) ? $user->hometown->name : null,
|
||||
'description' => isset($user->bio) ? $user->bio : null,
|
||||
'image' => 'https://graph.facebook.com/me/picture?type=normal&access_token='.$token->access_token,
|
||||
'urls' => array(
|
||||
'Facebook' => $user->link,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Facebook OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class Facebook extends Oauth2\Client\IDP
|
||||
{
|
||||
protected $scope = array('offline_access', 'email', 'read_stream');
|
||||
|
||||
public function urlAuthorize()
|
||||
{
|
||||
return 'https://www.facebook.com/dialog/oauth';
|
||||
}
|
||||
|
||||
public function urlAccessToken()
|
||||
{
|
||||
return 'https://graph.facebook.com/oauth/access_token';
|
||||
}
|
||||
|
||||
public function getUserInfo(Oauth2\Token\Access $token)
|
||||
{
|
||||
$url = 'https://graph.facebook.com/me?'.http_build_query(array(
|
||||
'access_token' => $token->access_token,
|
||||
));
|
||||
|
||||
$user = json_decode(file_get_contents($url));
|
||||
|
||||
return array(
|
||||
'uid' => $user->id,
|
||||
'nickname' => isset($user->username) ? $user->username : null,
|
||||
'name' => $user->name,
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'email' => isset($user->email) ? $user->email : null,
|
||||
'location' => isset($user->hometown->name) ? $user->hometown->name : null,
|
||||
'description' => isset($user->bio) ? $user->bio : null,
|
||||
'image' => 'https://graph.facebook.com/me/picture?type=normal&access_token='.$token->access_token,
|
||||
'urls' => array(
|
||||
'Facebook' => $user->link,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Foursquare OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class Foursquare extends Oauth2\Client\IDP
|
||||
{
|
||||
public $method = 'POST';
|
||||
|
||||
public function urlAuthorize()
|
||||
{
|
||||
return 'https://foursquare.com/oauth2/authenticate';
|
||||
}
|
||||
|
||||
public function urlAccessToken()
|
||||
{
|
||||
return 'https://foursquare.com/oauth2/access_token';
|
||||
}
|
||||
|
||||
public function getUserInfo(Oauth2\Token\Access $token)
|
||||
{
|
||||
$url = 'https://api.foursquare.com/v2/users/self?'.http_build_query(array(
|
||||
'oauth_token' => $token->access_token,
|
||||
));
|
||||
|
||||
$response = json_decode(file_get_contents($url));
|
||||
|
||||
$user = $response->response->user;
|
||||
|
||||
// Create a response from the request
|
||||
return array(
|
||||
'uid' => $user->id,
|
||||
'name' => sprintf('%s %s', $user->firstName, $user->lastName),
|
||||
'email' => $user->contact->email,
|
||||
'image' => $user->photo,
|
||||
'location' => $user->homeCity,
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* GitHub OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Github extends Oauth2\Client\IDP
|
||||
{
|
||||
public function urlAuthorize()
|
||||
{
|
||||
return 'https://github.com/login/oauth/authorize';
|
||||
}
|
||||
|
||||
public function urlAccessToken()
|
||||
{
|
||||
return 'https://github.com/login/oauth/access_token';
|
||||
}
|
||||
|
||||
public function getUserInfo(Oauth\Token\Access $token)
|
||||
{
|
||||
$url = 'https://api.github.com/user?'.http_build_query(array(
|
||||
'access_token' => $token->access_token,
|
||||
));
|
||||
|
||||
$user = json_decode(file_get_contents($url));
|
||||
|
||||
return array(
|
||||
'uid' => $user->id,
|
||||
'nickname' => $user->login,
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'urls' => array(
|
||||
'GitHub' => 'http://github.com/'.$user->login,
|
||||
'Blog' => $user->blog,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,84 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Google OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Google extends OAuth2_Provider
|
||||
{
|
||||
/**
|
||||
* @var string the method to use when requesting tokens
|
||||
*/
|
||||
public $method = 'POST';
|
||||
|
||||
/**
|
||||
* @var string scope separator, most use "," but some like Google are spaces
|
||||
*/
|
||||
public $scope_seperator = ' ';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://accounts.google.com/o/oauth2/auth';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://accounts.google.com/o/oauth2/token';
|
||||
}
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
// Now make sure we have the default scope to get user data
|
||||
empty($options['scope']) and $options['scope'] = array(
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
'https://www.googleapis.com/auth/userinfo.email'
|
||||
);
|
||||
|
||||
// Array it if its string
|
||||
$options['scope'] = (array) $options['scope'];
|
||||
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get access to the API
|
||||
*
|
||||
* @param string The access code
|
||||
* @return object Success or failure along with the response details
|
||||
*/
|
||||
public function access($code, $options = array())
|
||||
{
|
||||
if ($code === null)
|
||||
{
|
||||
throw new OAuth2_Exception(array('message' => 'Expected Authorization Code from '.ucfirst($this->name).' is missing'));
|
||||
}
|
||||
|
||||
return parent::access($code, $options);
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
$url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&'.http_build_query(array(
|
||||
'access_token' => $token->access_token,
|
||||
));
|
||||
|
||||
$user = json_decode(file_get_contents($url), true);
|
||||
return array(
|
||||
'uid' => $user['id'],
|
||||
'nickname' => url_title($user['name'], '_', true),
|
||||
'name' => $user['name'],
|
||||
'first_name' => $user['given_name'],
|
||||
'last_name' => $user['family_name'],
|
||||
'email' => $user['email'],
|
||||
'location' => null,
|
||||
'image' => (isset($user['picture'])) ? $user['picture'] : null,
|
||||
'description' => null,
|
||||
'urls' => array(),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Instagram OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Instagram extends OAuth2_Provider
|
||||
{
|
||||
/**
|
||||
* @var string scope separator, most use "," but some like Google are spaces
|
||||
*/
|
||||
public $scope_seperator = '+';
|
||||
|
||||
/**
|
||||
* @var string the method to use when requesting tokens
|
||||
*/
|
||||
public $method = 'POST';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://api.instagram.com/oauth/authorize';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://api.instagram.com/oauth/access_token';
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
$user = $token->user;
|
||||
|
||||
return array(
|
||||
'uid' => $user->id,
|
||||
'nickname' => $user->username,
|
||||
'name' => $user->full_name,
|
||||
'image' => $user->profile_picture,
|
||||
'urls' => array(
|
||||
'website' => $user->website,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Mailchimp OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Mailchimp extends OAuth2_Provider
|
||||
{
|
||||
/**
|
||||
* @var string the method to use when requesting tokens
|
||||
*/
|
||||
protected $method = 'POST';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://login.mailchimp.com/oauth2/authorize';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://login.mailchimp.com/oauth2/token';
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
// Create a response from the request
|
||||
return array(
|
||||
'uid' => $token->access_token,
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Mailru OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Lavr Lyndin
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Mailru extends OAuth2_Provider
|
||||
{
|
||||
public $method = 'POST';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://connect.mail.ru/oauth/authorize';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://connect.mail.ru/oauth/token';
|
||||
}
|
||||
|
||||
protected function sign_server_server(array $request_params, $secret_key)
|
||||
{
|
||||
ksort($request_params);
|
||||
$params = '';
|
||||
foreach ($request_params as $key => $value) {
|
||||
$params .= "$key=$value";
|
||||
}
|
||||
return md5($params . $secret_key);
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
$request_params = array(
|
||||
'app_id' => $this->client_id,
|
||||
'method' => 'users.getInfo',
|
||||
'uids' => $token->uid,
|
||||
'access_token' => $token->access_token,
|
||||
'secure' => 1
|
||||
);
|
||||
|
||||
$sig = $this->sign_server_server($request_params,$this->client_secret);
|
||||
$url = 'http://www.appsmail.ru/platform/api?'.http_build_query($request_params).'&sig='.$sig;
|
||||
|
||||
$user = json_decode(file_get_contents($url));
|
||||
|
||||
return array(
|
||||
'uid' => $user[0]->uid,
|
||||
'nickname' => $user[0]->nick,
|
||||
'name' => $user[0]->first_name.' '.$user[0]->last_name,
|
||||
'first_name' => $user[0]->first_name,
|
||||
'last_name' => $user[0]->last_name,
|
||||
'email' => isset($user[0]->email) ? $user[0]->email : null,
|
||||
'image' => isset($user[0]->pic_big) ? $user[0]->pic_big : null,
|
||||
);
|
||||
}
|
||||
|
||||
public function authorize($options = array())
|
||||
{
|
||||
$state = md5(uniqid(rand(), TRUE));
|
||||
get_instance()->session->set_userdata('state', $state);
|
||||
|
||||
$params = array(
|
||||
'client_id' => $this->client_id,
|
||||
'redirect_uri' => isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri,
|
||||
'response_type' => 'code',
|
||||
);
|
||||
|
||||
redirect($this->url_authorize().'?'.http_build_query($params));
|
||||
}
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* PayPal OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Paypal extends OAuth2_Provider
|
||||
{
|
||||
/**
|
||||
* @var string default scope (useful if a scope is required for user info)
|
||||
*/
|
||||
protected $scope = array('https://identity.x.com/xidentity/resources/profile/me');
|
||||
|
||||
/**
|
||||
* @var string the method to use when requesting tokens
|
||||
*/
|
||||
protected $method = 'POST';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://identity.x.com/xidentity/resources/authorize';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://identity.x.com/xidentity/oauthtokenservice';
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
$url = 'https://identity.x.com/xidentity/resources/profile/me?' . http_build_query(array(
|
||||
'oauth_token' => $token->access_token
|
||||
));
|
||||
|
||||
$user = json_decode(file_get_contents($url));
|
||||
$user = $user->identity;
|
||||
|
||||
return array(
|
||||
'uid' => $user['userId'],
|
||||
'nickname' => url_title($user['fullName'], '_', true),
|
||||
'name' => $user['fullName'],
|
||||
'first_name' => $user['firstName'],
|
||||
'last_name' => $user['lastName'],
|
||||
'email' => $user['emails'][0],
|
||||
'location' => $user->addresses[0],
|
||||
'image' => null,
|
||||
'description' => null,
|
||||
'urls' => array(
|
||||
'PayPal' => null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Soundcloud OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Soundcloud extends OAuth2_Provider
|
||||
{
|
||||
/**
|
||||
* @var string the method to use when requesting tokens
|
||||
*/
|
||||
protected $method = 'POST';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://soundcloud.com/connect';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://api.soundcloud.com/oauth2/token';
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
$url = 'https://api.soundcloud.com/me.json?'.http_build_query(array(
|
||||
'oauth_token' => $token->access_token,
|
||||
));
|
||||
|
||||
$user = json_decode(file_get_contents($url));
|
||||
|
||||
// Create a response from the request
|
||||
return array(
|
||||
'uid' => $user->id,
|
||||
'nickname' => $user->username,
|
||||
'name' => $user->full_name,
|
||||
'location' => $user->country.' ,'.$user->country,
|
||||
'description' => $user->description,
|
||||
'image' => $user->avatar_url,
|
||||
'urls' => array(
|
||||
'MySpace' => $user->myspace_name,
|
||||
'Website' => $user->website,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Vkontakte OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Lavr Lyndin
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Vkontakte extends OAuth2_Provider
|
||||
{
|
||||
protected $method = 'POST';
|
||||
public $uid_key = 'user_id';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'http://oauth.vk.com/authorize';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://oauth.vk.com/access_token';
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
$scope = array('nickname', 'screen_name','photo_big');
|
||||
$url = 'https://api.vk.com/method/users.get?'.http_build_query(array(
|
||||
'uids' => $token->uid,
|
||||
'fields' => implode(",",$scope),
|
||||
'access_token' => $token->access_token,
|
||||
));
|
||||
|
||||
$user = json_decode(file_get_contents($url))->response;
|
||||
|
||||
if(sizeof($user)==0)
|
||||
return null;
|
||||
else
|
||||
$user = $user[0];
|
||||
|
||||
return array(
|
||||
'uid' => $user->uid,
|
||||
'nickname' => isset($user->nickname) ? $user->nickname : null,
|
||||
'name' => isset($user->name) ? $user->name : null,
|
||||
'first_name' => isset($user->first_name) ? $user->first_name : null,
|
||||
'last_name' => isset($user->last_name) ? $user->last_name : null,
|
||||
'email' => null,
|
||||
'location' => null,
|
||||
'description' => null,
|
||||
'image' => isset($user->photo_big) ? $user->photo_big : null,
|
||||
'urls' => array(),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Windows Live OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2012 HappyNinjas Ltd
|
||||
* @license http://philsturgeon.co.uk/code/dbad-license
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Windowslive extends OAuth2_Provider
|
||||
{
|
||||
protected $scope = array('wl.basic', 'wl.emails');
|
||||
|
||||
/**
|
||||
* @var string the method to use when requesting tokens
|
||||
*/
|
||||
protected $method = 'POST';
|
||||
|
||||
// authorise url
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://oauth.live.com/authorize';
|
||||
}
|
||||
|
||||
// access token url
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://oauth.live.com/token';
|
||||
}
|
||||
|
||||
// get basic user information
|
||||
/********************************
|
||||
** this can be extended through the
|
||||
** use of scopes, check out the document at
|
||||
** http://msdn.microsoft.com/en-gb/library/hh243648.aspx#user
|
||||
*********************************/
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
// define the get user information token
|
||||
$url = 'https://apis.live.net/v5.0/me?'.http_build_query(array(
|
||||
'access_token' => $token->access_token,
|
||||
));
|
||||
|
||||
// perform network request
|
||||
$user = json_decode(file_get_contents($url));
|
||||
|
||||
// create a response from the request and return it
|
||||
return array(
|
||||
'uid' => $user->id,
|
||||
'name' => $user->name,
|
||||
'nickname' => url_title($user->name, '_', true),
|
||||
// 'location' => $user[''], # scope wl.postal_addresses is required
|
||||
# but won't be implemented by default
|
||||
'locale' => $user->locale,
|
||||
'urls' => array('Windows Live' => $user->link),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,115 +0,0 @@
|
||||
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
/**
|
||||
* Yandex OAuth2 Provider
|
||||
*
|
||||
* @package CodeIgniter/OAuth2
|
||||
* @category Provider
|
||||
* @author Lavr Lyndin
|
||||
*/
|
||||
|
||||
class OAuth2_Provider_Yandex extends OAuth2_Provider
|
||||
{
|
||||
public $method = 'POST';
|
||||
|
||||
public function url_authorize()
|
||||
{
|
||||
return 'https://oauth.yandex.ru/authorize';
|
||||
}
|
||||
|
||||
public function url_access_token()
|
||||
{
|
||||
return 'https://oauth.yandex.ru/token';
|
||||
}
|
||||
|
||||
public function get_user_info(OAuth2_Token_Access $token)
|
||||
{
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => 'GET',
|
||||
'header' => 'Authorization: OAuth '.$token->access_token
|
||||
)
|
||||
);
|
||||
$_default_opts = stream_context_get_params(stream_context_get_default());
|
||||
|
||||
$opts = array_merge_recursive($_default_opts['options'], $opts);
|
||||
$context = stream_context_create($opts);
|
||||
$url = 'http://api-yaru.yandex.ru/me/?format=json';
|
||||
|
||||
$user = json_decode(file_get_contents($url,false,$context));
|
||||
|
||||
preg_match("/\d+$/",$user->id,$uid);
|
||||
|
||||
return array(
|
||||
'uid' => $uid[0],
|
||||
'nickname' => isset($user->name) ? $user->name : null,
|
||||
'name' => isset($user->name) ? $user->name : null,
|
||||
'first_name' => isset($user->first_name) ? $user->first_name : null,
|
||||
'last_name' => isset($user->last_name) ? $user->last_name : null,
|
||||
'email' => isset($user->email) ? $user->email : null,
|
||||
'location' => isset($user->hometown->name) ? $user->hometown->name : null,
|
||||
'description' => isset($user->bio) ? $user->bio : null,
|
||||
'image' => $user->links->userpic,
|
||||
);
|
||||
}
|
||||
|
||||
public function access($code, $options = array())
|
||||
{
|
||||
$params = array(
|
||||
'client_id' => $this->client_id,
|
||||
'client_secret' => $this->client_secret,
|
||||
'grant_type' => isset($options['grant_type']) ? $options['grant_type'] : 'authorization_code',
|
||||
);
|
||||
|
||||
switch ($params['grant_type'])
|
||||
{
|
||||
case 'authorization_code':
|
||||
$params['code'] = $code;
|
||||
$params['redirect_uri'] = isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri;
|
||||
break;
|
||||
|
||||
case 'refresh_token':
|
||||
$params['refresh_token'] = $code;
|
||||
break;
|
||||
}
|
||||
|
||||
$response = null;
|
||||
$url = $this->url_access_token();
|
||||
|
||||
$curl = curl_init($url);
|
||||
|
||||
$headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8;';
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// curl_setopt($curl, CURLOPT_USERAGENT, 'yamolib-php');
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, 80);
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
// curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
|
||||
// curl_setopt($curl, CURLOPT_CAINFO, dirname(__FILE__) . '/../data/ca-certificate.crt');
|
||||
|
||||
$response = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
$return = json_decode($response, true);
|
||||
|
||||
if ( ! empty($return['error']))
|
||||
{
|
||||
throw new OAuth2_Exception($return);
|
||||
}
|
||||
|
||||
switch ($params['grant_type'])
|
||||
{
|
||||
case 'authorization_code':
|
||||
return OAuth2_Token::factory('access', $return);
|
||||
break;
|
||||
|
||||
case 'refresh_token':
|
||||
return OAuth2_Token::factory('refresh', $return);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Oauth2\Client;
|
||||
|
||||
abstract class Token
|
||||
{
|
||||
|
||||
/**
|
||||
* Create a new token object.
|
||||
*
|
||||
* @param string token type
|
||||
* @param array token options
|
||||
* @return Token
|
||||
*/
|
||||
public static function factory($name = 'access', array $options = null)
|
||||
{
|
||||
include_once 'Token/'.ucfirst(strtolower($name)).'.php';
|
||||
|
||||
$class = 'Oauth2\Client\Token\\'.ucfirst($name);
|
||||
|
||||
return new $class($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of any protected class variable.
|
||||
*
|
||||
* @param string variable name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a boolean if the property is set
|
||||
*
|
||||
* @param string variable name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return isset($this->$key);
|
||||
}
|
||||
|
||||
} // End Token
|
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Oauth2\Client\Token;
|
||||
|
||||
/**
|
||||
* OAuth2 Token
|
||||
*
|
||||
* @package OAuth2
|
||||
* @category Token
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2011 HappyNinjas Ltd
|
||||
*/
|
||||
|
||||
class Access extends \Oauth2\Client\Token
|
||||
{
|
||||
/**
|
||||
* @var string accessToken
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var int expires
|
||||
*/
|
||||
protected $expires;
|
||||
|
||||
/**
|
||||
* @var string refreshToken
|
||||
*/
|
||||
protected $refreshToken;
|
||||
|
||||
/**
|
||||
* @var string uid
|
||||
*/
|
||||
protected $uid;
|
||||
|
||||
/**
|
||||
* Sets the token, expiry, etc values.
|
||||
*
|
||||
* @param array token options
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $options = null)
|
||||
{
|
||||
if ( ! isset($options['access_token'])) {
|
||||
throw new \BadMethodCallException('Required option not passed: access_token'.PHP_EOL.print_r($options, true));
|
||||
}
|
||||
|
||||
$this->accessToken = $options['access_token'];
|
||||
|
||||
// Some providers (not many) give the uid here, so lets take it
|
||||
isset($options['uid']) and $this->uid = $options['uid'];
|
||||
|
||||
//Vkontakte uses user_id instead of uid
|
||||
isset($options['user_id']) and $this->uid = $options['user_id'];
|
||||
|
||||
//Mailru uses x_mailru_vid instead of uid
|
||||
isset($options['x_mailru_vid']) and $this->uid = $options['x_mailru_vid'];
|
||||
|
||||
// We need to know when the token expires, add num. seconds to current time
|
||||
isset($options['expires_in']) and $this->expires = time() + ((int) $options['expires_in']);
|
||||
|
||||
// Facebook is just being a spec ignoring jerk
|
||||
isset($options['expires']) and $this->expires = time() + ((int) $options['expires']);
|
||||
|
||||
// Grab a refresh token so we can update access tokens when they expires
|
||||
isset($options['refresh_token']) and $this->refreshToken = $options['refresh_token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->accessToken;
|
||||
}
|
||||
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth2 Token
|
||||
*
|
||||
* @package OAuth2
|
||||
* @category Token
|
||||
* @author Phil Sturgeon
|
||||
* @copyright (c) 2011 HappyNinjas Ltd
|
||||
*/
|
||||
|
||||
class Authorize extends \Oauth2\Client\Token
|
||||
{
|
||||
/**
|
||||
* @var string code
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @var string redirect_uri
|
||||
*/
|
||||
protected $redirectUri;
|
||||
|
||||
/**
|
||||
* Sets the token, expiry, etc values.
|
||||
*
|
||||
* @param array token options
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $options)
|
||||
{
|
||||
if ( ! isset($options['code'])) {
|
||||
|
||||
throw new Exception('Required option not passed: code');
|
||||
|
||||
} elseif ( ! isset($options['redirect_uri'])) {
|
||||
|
||||
throw new Exception('Required option not passed: redirect_uri');
|
||||
|
||||
}
|
||||
|
||||
$this->code = $options['code'];
|
||||
$this->redirectUri = $options['redirect_uri'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->code;
|
||||
}
|
||||
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Oauth2\Resource;
|
||||
|
||||
interface Database
|
||||
{
|
||||
/**
|
||||
* Validate an access token and return the session details.
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* SELECT id, owner_type, owner_id FROM oauth_sessions WHERE access_token =
|
||||
* $accessToken AND stage = 'granted' AND
|
||||
* access_token_expires > UNIX_TIMESTAMP(now())
|
||||
* </code>
|
||||
*
|
||||
* Response:
|
||||
*
|
||||
* <code>
|
||||
* Array
|
||||
* (
|
||||
* [id] => (int) The session ID
|
||||
* [owner_type] => (string) The session owner type
|
||||
* [owner_id] => (string) The session owner's ID
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @param string $accessToken The access token
|
||||
* @return array|bool Return an array on success or false on failure
|
||||
*/
|
||||
public function validateAccessToken($accessToken);
|
||||
|
||||
/**
|
||||
* Returns the scopes that the session is authorised with.
|
||||
*
|
||||
* Database query:
|
||||
*
|
||||
* <code>
|
||||
* SELECT scope FROM oauth_session_scopes WHERE session_id =
|
||||
* $sessionId
|
||||
* </code>
|
||||
*
|
||||
* Response:
|
||||
*
|
||||
* <code>
|
||||
* Array
|
||||
* (
|
||||
* [0] => (string) A scope
|
||||
* [1] => (string) Another scope
|
||||
* ...
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* @param int $sessionId The session ID
|
||||
* @return array A list of scopes
|
||||
*/
|
||||
public function sessionScopes($sessionId);
|
||||
}
|
@@ -1,253 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Oauth2\Resource;
|
||||
|
||||
class ServerException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class ClientException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* Reference to the database abstractor
|
||||
* @var object
|
||||
*/
|
||||
private $_db = null;
|
||||
|
||||
/**
|
||||
* The access token.
|
||||
* @access private
|
||||
*/
|
||||
private $_accessToken = null;
|
||||
|
||||
/**
|
||||
* The scopes the access token has access to.
|
||||
* @access private
|
||||
*/
|
||||
private $_scopes = array();
|
||||
|
||||
/**
|
||||
* The type of owner of the access token.
|
||||
* @access private
|
||||
*/
|
||||
private $_type = null;
|
||||
|
||||
/**
|
||||
* The ID of the owner of the access token.
|
||||
* @access private
|
||||
*/
|
||||
private $_typeId = null;
|
||||
|
||||
/**
|
||||
* Server configuration
|
||||
* @var array
|
||||
*/
|
||||
private $_config = array(
|
||||
'token_key' => 'oauth_token'
|
||||
);
|
||||
|
||||
/**
|
||||
* Error codes.
|
||||
*
|
||||
* To provide i8ln errors just overwrite the keys
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $errors = array(
|
||||
'missing_access_token' => 'An access token was not presented with the request',
|
||||
'invalid_access_token' => 'The access token is not registered with the resource server',
|
||||
'missing_access_token_details' => 'The registered database abstractor did not return a valid access token details response',
|
||||
'invalid_access_token_scopes' => 'The registered database abstractor did not return a valid access token scopes response',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($options = null)
|
||||
{
|
||||
if ($options !== null) {
|
||||
$this->_config = array_merge($this->_config, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to test if access token represents a particular owner type
|
||||
* @param string $method The method name
|
||||
* @param mixed $arguements The method arguements
|
||||
* @return bool If method is valid, and access token is owned by the requested party then true,
|
||||
*/
|
||||
public function __call($method, $arguements = null)
|
||||
{
|
||||
if (substr($method, 0, 2) === 'is') {
|
||||
|
||||
if ($this->_type === strtolower(substr($method, 2))) {
|
||||
return $this->_typeId;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
trigger_error('Call to undefined function ' . $method . '()');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a database abstrator class
|
||||
*
|
||||
* @access public
|
||||
* @param object $db A class that implements OAuth2ServerDatabase
|
||||
* @return void
|
||||
*/
|
||||
public function registerDbAbstractor($db)
|
||||
{
|
||||
$this->_db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init function
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$accessToken = null;
|
||||
|
||||
$_SERVER['REQUEST_METHOD'] = isset($_SERVER['REQUEST_METHOD']) ?
|
||||
$_SERVER['REQUEST_METHOD'] :
|
||||
null;
|
||||
|
||||
// Try and get the access token via an access_token or oauth_token parameter
|
||||
switch ($_SERVER['REQUEST_METHOD'])
|
||||
{
|
||||
case 'POST':
|
||||
$accessToken = isset($_POST[$this->_config['token_key']]) ?
|
||||
$_POST[$this->_config['token_key']] :
|
||||
null;
|
||||
break;
|
||||
|
||||
default:
|
||||
$accessToken = isset($_GET[$this->_config['token_key']]) ?
|
||||
$_GET[$this->_config['token_key']] :
|
||||
null;
|
||||
break;
|
||||
}
|
||||
|
||||
// Try and get an access token from the auth header
|
||||
if (function_exists('getallheaders')) {
|
||||
|
||||
$headers = getallheaders();
|
||||
|
||||
if (isset($headers['Authorization'])) {
|
||||
|
||||
$rawToken = base64_decode(str_replace('Bearer ', '', trim($headers['Authorization'])));
|
||||
|
||||
if ( ! empty($rawToken)) {
|
||||
$accessToken = $rawToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($accessToken) {
|
||||
|
||||
$result = $this->_dbCall('validateAccessToken', $accessToken);
|
||||
|
||||
if ($result === false) {
|
||||
|
||||
throw new ClientException($this->errors['invalid_access_token']);
|
||||
|
||||
} else {
|
||||
|
||||
if ( ! array_key_exists('id', $result) ||
|
||||
! array_key_exists('owner_id', $result) ||
|
||||
! array_key_exists('owner_type', $result)) {
|
||||
throw new ServerException($this->errors['missing_access_token_details']);
|
||||
}
|
||||
|
||||
$this->_accessToken = $accessToken;
|
||||
$this->_type = $result['owner_type'];
|
||||
$this->_typeId = $result['owner_id'];
|
||||
|
||||
// Get the scopes
|
||||
$scopes = $this->_dbCall('sessionScopes', $result['id']);
|
||||
|
||||
if ( ! is_array($scopes))
|
||||
{
|
||||
throw new ServerException($this->errors['invalid_access_token_scopes']);
|
||||
}
|
||||
|
||||
$this->_scopes = $scopes;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
throw new ClientException($this->errors['missing_access_token']);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the access token has a specific scope
|
||||
*
|
||||
* @param mixed $scopes Scope(s) to check
|
||||
*
|
||||
* @access public
|
||||
* @return string|bool
|
||||
*/
|
||||
public function hasScope($scopes)
|
||||
{
|
||||
if (is_string($scopes)) {
|
||||
|
||||
if (in_array($scopes, $this->_scopes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} elseif (is_array($scopes)) {
|
||||
|
||||
foreach ($scopes as $scope) {
|
||||
|
||||
if ( ! in_array($scope, $this->_scopes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call database methods from the abstractor
|
||||
*
|
||||
* @return mixed The query result
|
||||
*/
|
||||
private function _dbCall()
|
||||
{
|
||||
if ($this->_db === null) {
|
||||
throw new ServerException('No registered database abstractor');
|
||||
}
|
||||
|
||||
if ( ! $this->_db instanceof Database) {
|
||||
throw new ServerException('The registered database abstractor is not an instance of Oauth2\Resource\Database');
|
||||
}
|
||||
|
||||
$args = func_get_args();
|
||||
$method = $args[0];
|
||||
unset($args[0]);
|
||||
$params = array_values($args);
|
||||
|
||||
return call_user_func_array(array($this->_db, $method), $params);
|
||||
}
|
||||
}
|
57
src/Repositories/AccessTokenRepositoryInterface.php
Normal file
57
src/Repositories/AccessTokenRepositoryInterface.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
|
||||
|
||||
/**
|
||||
* Access token interface.
|
||||
*/
|
||||
interface AccessTokenRepositoryInterface extends RepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Create a new access token
|
||||
*
|
||||
* @param ClientEntityInterface $clientEntity
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
* @param mixed $userIdentifier
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null);
|
||||
|
||||
/**
|
||||
* Persists a new access token to permanent storage.
|
||||
*
|
||||
* @param AccessTokenEntityInterface $accessTokenEntity
|
||||
*
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*/
|
||||
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity);
|
||||
|
||||
/**
|
||||
* Revoke an access token.
|
||||
*
|
||||
* @param string $tokenId
|
||||
*/
|
||||
public function revokeAccessToken($tokenId);
|
||||
|
||||
/**
|
||||
* Check if the access token has been revoked.
|
||||
*
|
||||
* @param string $tokenId
|
||||
*
|
||||
* @return bool Return true if this token has been revoked
|
||||
*/
|
||||
public function isAccessTokenRevoked($tokenId);
|
||||
}
|
51
src/Repositories/AuthCodeRepositoryInterface.php
Normal file
51
src/Repositories/AuthCodeRepositoryInterface.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
|
||||
|
||||
/**
|
||||
* Auth code storage interface.
|
||||
*/
|
||||
interface AuthCodeRepositoryInterface extends RepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Creates a new AuthCode
|
||||
*
|
||||
* @return AuthCodeEntityInterface
|
||||
*/
|
||||
public function getNewAuthCode();
|
||||
|
||||
/**
|
||||
* Persists a new auth code to permanent storage.
|
||||
*
|
||||
* @param AuthCodeEntityInterface $authCodeEntity
|
||||
*
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*/
|
||||
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity);
|
||||
|
||||
/**
|
||||
* Revoke an auth code.
|
||||
*
|
||||
* @param string $codeId
|
||||
*/
|
||||
public function revokeAuthCode($codeId);
|
||||
|
||||
/**
|
||||
* Check if the auth code has been revoked.
|
||||
*
|
||||
* @param string $codeId
|
||||
*
|
||||
* @return bool Return true if this code has been revoked
|
||||
*/
|
||||
public function isAuthCodeRevoked($codeId);
|
||||
}
|
38
src/Repositories/ClientRepositoryInterface.php
Normal file
38
src/Repositories/ClientRepositoryInterface.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
|
||||
/**
|
||||
* Client storage interface.
|
||||
*/
|
||||
interface ClientRepositoryInterface extends RepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Get a client.
|
||||
*
|
||||
* @param string $clientIdentifier The client's identifier
|
||||
*
|
||||
* @return ClientEntityInterface|null
|
||||
*/
|
||||
public function getClientEntity($clientIdentifier);
|
||||
|
||||
/**
|
||||
* Validate a client's secret.
|
||||
*
|
||||
* @param string $clientIdentifier The client's identifier
|
||||
* @param null|string $clientSecret The client's secret (if sent)
|
||||
* @param null|string $grantType The type of grant the client is using (if sent)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateClient($clientIdentifier, $clientSecret, $grantType);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user