Slide 1

Slide 1 text

JWT To infinity
 & beyond! authentication Luís Cobucci
 @lcobucci https://goo.gl/gbd3H5

Slide 2

Slide 2 text

Tokens? https://goo.gl/C0LI6F

Slide 3

Slide 3 text

Browser Server DB

Slide 4

Slide 4 text

Browser Server 1. presents credentials POST /login 
 {
 "email": "[email protected]", "password": "amazing!"
 } DB

Slide 5

Slide 5 text

Browser Server DB 1. presents credentials 2. validates and starts a session

Slide 6

Slide 6 text

Browser Server DB 1. presents credentials 2. validates and starts a session 200 OK Set-Cookie: PHPSESSIONID=ABC123; Domain=foo.bar; Secure; HttpOnly; Expires=Thu, 1 Jun 2017 12:00:00 GMT

Slide 7

Slide 7 text

Browser Server DB 1. presents credentials 2. validates and starts a session 200 OK Set-Cookie: PHPSESSIONID=ABC123; Domain=foo.bar; Secure; HttpOnly; Expires=Thu, 1 Jun 2017 12:00:00 GMT

Slide 8

Slide 8 text

Browser Server DB 1. presents credentials 2. validates and starts a session 3. sends cookies on next requests GET / Cookie: PHPSESSIONID=ABC123

Slide 9

Slide 9 text

Browser Server DB 1. presents credentials 2. validates and starts a session 3. sends cookies on next requests 4. reads session data and returns a specific response for logged user 200 OK Hello John!

Slide 10

Slide 10 text

“ (…) Each request from any client contains all the information necessary to service the request, and session state is held in the client. Representational State Transfer - Wikipedia

Slide 11

Slide 11 text

A mission http://goo.gl/RfVHNu

Slide 12

Slide 12 text

{ "token": "abc123", "uid": 1, "expiration": "…",
 "scope": ["a", "b", "c"] }

Slide 13

Slide 13 text

{ "token": "abc123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] }

Slide 14

Slide 14 text

{ "token": "abc123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] }

Slide 15

Slide 15 text

{ "token": "abc123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "def789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "ghi123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "jkl789", "uid": 6,
 "expiration": "…", "scope": ["a"] }

Slide 16

Slide 16 text

{ "token": "abc123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "def789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "ghi123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "jkl789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "abc123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "def789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "ghi123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "jkl789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "abc123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "def789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "ghi123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "jkl789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "abc123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "def456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "abc789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "def789", "uid": 6,
 "expiration": "…", "scope": ["a"] } { "token": "ghi123", "uid": 1,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl123", "uid": 2,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi456", "uid": 3,
 "expiration": "…", "scope": ["a", "b", "c"] } { "token": "jkl456", "uid": 4,
 "expiration": "…", "scope": ["a", "b"] } { "token": "ghi789", "uid": 5,
 "expiration": "…", "scope": ["a", "b"] } { "token": "jkl789", "uid": 6,
 "expiration": "…", "scope": ["a"] }

Slide 17

Slide 17 text

Wonderful, right? http://goo.gl/9jQFkj

Slide 18

Slide 18 text

No! http://goo.gl/XHI1fw

Slide 19

Slide 19 text

We need
 SIMPLE ! it

Slide 20

Slide 20 text

Luís Cobucci
 @lcobucci

Slide 21

Slide 21 text

JOSE JSON Object Signing and Encryption

Slide 22

Slide 22 text

JOSE JSON Object Signing and Encryption jws jwa jwt jwe jwk

Slide 23

Slide 23 text

eyJ0eXAiOiJKV1QiLCJhbGciOiJ IUzI1NiJ9.eyJ1c2VyIjp7ImlkI joxLCJuYW1lIjoiTHXDrXMgQ29i dWNjaSJ9fQ.

Slide 24

Slide 24 text

eyJ0eXAiOiJKV1QiLCJhbGciOiJ IUzI1NiJ9.eyJ1c2VyIjp7ImlkI joxLCJuYW1lIjoiTHXDrXMgQ29i dWNjaSJ9fQ. base64_encode()

Slide 25

Slide 25 text

eyJ0eXAiOiJKV1QiLCJhbGciOiJ IUzI1NiJ9.eyJ1c2VyIjp7ImlkI joxLCJuYW1lIjoiTHXDrXMgQ29i dWNjaSJ9fQ.

Slide 26

Slide 26 text

eyJ0eXAiOiJKV1QiLCJhbGciOiJ IUzI1NiJ9 . eyJ1c2VyIjp7ImlkIjoxLCJuYW1 lIjoiTHXDrXMgQ29idWNjaSJ9fQ .

Slide 27

Slide 27 text

Base64URL eyJ0eXAiOiJKV1QiLCJhbGciOiJ IUzI1NiJ9 . eyJ1c2VyIjp7ImlkIjoxLCJuYW1 lIjoiTHXDrXMgQ29idWNjaSJ9fQ .

Slide 28

Slide 28 text

+ → -
 / → _
 = → (removed) TeSJWlQ/ S4YaOgK5tz7j+3KxBA g3HTONa9NP80R+9mY= TeSJWlQ_S4YaOgK5tz7 j-3KxBAg3HTONa9NP80 R-9mY

Slide 29

Slide 29 text

function base64url_encode(string $data): string { $data = base64_encode($data); 
 return rtrim(
 strtr($data, '+/', '-_'), '=' ); }

Slide 30

Slide 30 text

function base64url_decode(string $data): string {
 if ($remainder = strlen($data) % 4) { $data .= str_repeat('=', 4 - $remainder); } return base64_decode(
 strtr($data, '-_', '+/') ); }

Slide 31

Slide 31 text

eyJ0eXAiOiJKV1QiLCJhbGciOiJ IUzI1NiJ9 . eyJ1c2VyIjp7ImlkIjoxLCJuYW1 lIjoiTHXDrXMgQ29idWNjaSJ9fQ .

Slide 32

Slide 32 text

{
 "typ": "JWT",
 "alg": "none"
 } {
 "user": {
 "id": 1,
 "name": "Luís Cobucci"
 }
 }

Slide 33

Slide 33 text

The JSON! http://goo.gl/gH0hsx

Slide 34

Slide 34 text

401
 Unauthorised http://goo.gl/yyZ7oC

Slide 35

Slide 35 text

Client API DB

Slide 36

Slide 36 text

1. presents credentials POST /auth 
 {
 "email": "[email protected]", "password": "amazing!"
 } Client API DB

Slide 37

Slide 37 text

1. presents credentials 2. validates and creates a token Client API - issuer: auth.example.com - permitted to: client.example.com - expires in 300 seconds DB

Slide 38

Slide 38 text

1. presents credentials 2. validates and creates a token 201 Created 
 {
 "token": "…"
 } Client API DB

Slide 39

Slide 39 text

1. presents credentials 2. validates and creates a token 3. sends the issued token GET / Authorization: … Client API DB

Slide 40

Slide 40 text

1. presents credentials 2. validates and creates a token 3. sends the issued token 4. verifies the signature, validates the claims and processes the request - is it valid? - client allowed? - expected issuer? - can it be used at this moment? Client API DB

Slide 41

Slide 41 text

How about OAuth2?

Slide 42

Slide 42 text

Sessions https://goo.gl/KNrl16

Slide 43

Slide 43 text

file Webserver Set-Cookie:
 SESSION_ID=123abc

Slide 44

Slide 44 text

file a Webserver Client SESSION_ID=123abc

Slide 45

Slide 45 text

file a Webserver 1 Client SESSION_ID=123abc file b Webserver 2 file c Webserver 3 Load balancer

Slide 46

Slide 46 text

Webserver 1 Client SESSION=eyJ0eXAiOiJKV1QiLCJhbGciO iJIUzI1NiJ9.eyJ1c2VyIjp7ImlkIjoxL CJuYW1lIjoiTHXDrXMgQ29idWNjaSJ9fQ .hv9V7gBBJPeWMbwFFmRP7clLuof7r9fV JzZbLZIxBTs Webserver 2 Webserver 3 Load balancer

Slide 47

Slide 47 text

! 1. cannot store private information in the session
 
 2. sessions cannot be invalidated 
 3. increased network traffic
 
 4. race conditions with highly concurrent HTTP requests writing to session
 
 5. limit on the amount of data stored in session

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

More!! https://goo.gl/gEjEMm

Slide 50

Slide 50 text

https://goo.gl/GB6YkQ Let’s investigate it

Slide 51

Slide 51 text

{
 "typ": "JWT",
 "alg": "none"
 } {
 "user": {
 "id": 1,
 "name": "Luís Cobucci"
 }
 }

Slide 52

Slide 52 text

headers {
 "typ": "JWT",
 "alg": "none"
 } {
 "user": {
 "id": 1,
 "name": "Luís Cobucci"
 }
 }

Slide 53

Slide 53 text

headers {
 "typ": "JWT",
 "alg": "none"
 } {
 "user": {
 "id": 1,
 "name": "Luís Cobucci"
 }
 } claims

Slide 54

Slide 54 text

Base64URL( )
 + "." + 
 Base64URL( )
 + "." headers claims

Slide 55

Slide 55 text

headers {
 "typ": "JWT",
 "alg": "none"
 } {
 "user": {
 "id": 1,
 "name": "Luís Cobucci"
 }
 } claims

Slide 56

Slide 56 text

eyJhbGciOiJub25lIiwidHlwIjo iSldUIn0
 .
 eyJ1c2VyIjp7ImlkIjoxLCJuYW1 lIjoiTHXDrXMgQ29idWNjaSJ9fQ
 .

Slide 57

Slide 57 text

Can you trust it? https://goo.gl/EeeIdu

Slide 58

Slide 58 text

Base64URL( )
 + "." + 
 Base64URL( ) headers claims payload

Slide 59

Slide 59 text

Base64URL( )
 + "." + 
 Base64URL( ) headers claims payload = alg( , ) payload key signature

Slide 60

Slide 60 text

Base64URL( )
 + "." + 
 Base64URL( )
 + "." + 
 Base64URL( ) headers claims signature

Slide 61

Slide 61 text

function jwt_create( array $headers, array $claims, string $key ): string {
 $headers = base64url_encode(json_encode($headers));
 $claims = base64url_encode(json_encode($claims)); $payload = $headers . '.' . $claims; $signature = base64url_encode(
 hash_hmac('sha256', $payload, $key, true)
 ); return $payload . '.' . $signature; }

Slide 62

Slide 62 text

What algorithms? https://goo.gl/qNTg3D

Slide 63

Slide 63 text

HS256 HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512 none HMAC RSA ECDSA RSASSA-PSS

Slide 64

Slide 64 text

HS256 HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512 none HMAC RSA ECDSA RSASSA-PSS

Slide 65

Slide 65 text

HS256 HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512 none HMAC RSA ECDSA RSASSA-PSS

Slide 66

Slide 66 text

headers {
 "typ": "JWT",
 "alg": "HS256"
 } {
 "user": {
 "id": 1,
 "name": "Luís Cobucci"
 }
 } claims key Hello JWT+JWS!

Slide 67

Slide 67 text

eyJhbGciOiJub25lIiwidHlwIjo iSldUIn0
 .
 eyJ1c2VyIjp7ImlkIjoxLCJuYW1 lIjoiTHXDrXMgQ29idWNjaSJ9fQ
 .
 hv9V7gBBJPeWMbwFFmRP7clLuof 7r9fVJzZbLZIxBTs

Slide 68

Slide 68 text

How much data? https://goo.gl/eHFQwO

Slide 69

Slide 69 text

iat (NumericDate) exp (NumericDate) nbf (NumericDate) jti (String) iss (StringOrUri) aud (StringOrUri[]) sub (StringOrUri)

Slide 70

Slide 70 text

iat (NumericDate) exp (NumericDate) nbf (NumericDate) jti (String) iss (StringOrUri) aud (StringOrUri[]) sub (StringOrUri) case-sensitive

Slide 71

Slide 71 text

https://goo.gl/bkXMeq Revoke tokens?

Slide 72

Slide 72 text

PHP libraries! https://goo.gl/bGP8u8

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

declare(strict_types=1); require 'vendor/autoload.php'; use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Hmac\Sha256; return Configuration::forSymmetricSigner( new Sha256(), new Key('my super secret key') );

Slide 76

Slide 76 text

declare(strict_types=1); require 'vendor/autoload.php'; use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Rsa\Sha256; return Configuration::forAsymmetricSigner( new Sha256(), new Key('file://private.pem', 'testing'), new Key('file://public.pem') );

Slide 77

Slide 77 text

declare(strict_types=1); /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $signer = $config->getSigner(); $key = $config->getSigningKey(); $token = $config->createBuilder() ->withClaim('uid', 1) ->getToken($signer, $key);

Slide 78

Slide 78 text

declare(strict_types=1); /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $signer = $config->getSigner(); $key = $config->getSigningKey(); $token = $config->createBuilder() ->withClaim('uid', 1) ->identifiedBy(bin2hex(random_bytes(16))) ->getToken($signer, $key);

Slide 79

Slide 79 text

declare(strict_types=1); /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $signer = $config->getSigner(); $key = $config->getSigningKey(); $token = $config->createBuilder() ->withClaim('uid', 1) ->identifiedBy(bin2hex(random_bytes(16))) ->issuedBy('https://foo.bar') ->getToken($signer, $key);

Slide 80

Slide 80 text

declare(strict_types=1); /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $signer = $config->getSigner(); $key = $config->getSigningKey(); $token = $config->createBuilder() ->withClaim('uid', 1) ->identifiedBy(bin2hex(random_bytes(16))) ->issuedBy('https://foo.bar')
 ->permittedFor('https://client1.bar')
 ->permittedFor('https://client2.bar') ->getToken($signer, $key);

Slide 81

Slide 81 text

declare(strict_types=1); /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $signer = $config->getSigner(); $key = $config->getSigningKey(); $now = new DateTimeImmutable(); $token = $config->createBuilder() ->withClaim('uid', 1) ->identifiedBy(bin2hex(random_bytes(16))) ->issuedBy('https://foo.bar')
 ->permittedFor('https://client1.bar')
 ->permittedFor('https://client2.bar')
 ->issuedAt($now)
 ->canOnlyBeUsedAfter($now->modify('+5 minutes'))
 ->expiresAt($now->modify('+1 hour')) ->getToken($signer, $key);

Slide 82

Slide 82 text

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9
 .
 eyJ1aWQiOjEsImp0aSI6ImFhMzk3YzA2ZDUwZmRhNjYyYWUwZGE4OTU2ODdmN zY3IiwiaXNzIjoiaHR0cHM6Ly9mb28uYmFyIiwiYXVkIjpbImh0dHBzOi8vY2 xpZW50MS5iYXIiLCJodHRwczovL2NsaWVudDIuYmFyIl0sImlhdCI6IjE0OTU xNzg5MDQuODY4ODc5IiwibmJmIjoiMTQ5NTE3OTIwNC44Njg4NzkiLCJleHAi OiIxNDk1MTgyNTA0Ljg2ODg3OSJ9
 .
 jwXzXjm8cU92yxP3XcENg_ZnDvW1MkRTzSoaAwOYCTlSdQ5rv- dCLn_7_XPLHSuiACt_aFTnB093GYTpJQKRnqIFPYteK2jVnQALXNPxntnp- v6SMiFBxofCaVSjgKTWdqkWB4agWrTR77HK_iKdFoZMIdpr8UUBJatkc_MCoD vDMtuDRXwIEBfjs9baICtBvTZyDD7iiMmF4F_qvp2mWd_Qy93gZCrePKAJsgY - sujg84iQFOs-6I3GjybzA0U0Y_bTmCmQHfhRUX5_gL21bZxBFef38OFKW73Vx ehBxM4Ok_nWRbGY7ehsMBshXkJQfp97TJ1cV35a9zyAVXC04A

Slide 83

Slide 83 text

{
 "typ": "JWT",
 "alg": "RS256"
 } {
 "uid": 1, "jti": "aa397c06d50fda662ae0da895687f767", "iss": "https://foo.bar", "aud": ["https://client1.bar", “https://client2.bar”], "iat": "1495178904.868879", "nbf": "1495179204.868879", "exp": "1495182504.868879"
 }

Slide 84

Slide 84 text

declare(strict_types=1); /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1aWQiOjEsImp0aSI6ImFh' . 'Mzk3YzA2ZDUwZmRhNjYyYWUwZGE4OTU2ODdmNzY3IiwiaXNzIjoiaHR0cHM6L' . 'y9mb28uYmFyIiwiYXVkIjpbImh0dHBzOi8vY2xpZW50MS5iYXIiLCJodHRwcz' . 'ovL2NsaWVudDIuYmFyIl0sImlhdCI6IjE0OTUxNzg5MDQuODY4ODc5IiwibmJ'
 . 'mIjoiMTQ5NTE3OTIwNC44Njg4NzkiLCJleHAiOiIxNDk1MTgyNTA0Ljg2ODg3' . 'OSJ9.jwXzXjm8cU92yxP3XcENg_ZnDvW1MkRTzSoaAwOYCTlSdQ5rv-dCLn_7'
 . '_XPLHSuiACt_aFTnB093GYTpJQKRnqIFPYteK2jVnQALXNPxntnp-v6SMiFBx'
 . 'ofCaVSjgKTWdqkWB4agWrTR77HK_iKdFoZMIdpr8UUBJatkc_MCoDvDMtuDRX'
 . 'wIEBfjs9baICtBvTZyDD7iiMmF4F_qvp2mWd_Qy93gZCrePKAJsgY-sujg84i'
 . 'QFOs-6I3GjybzA0U0Y_bTmCmQHfhRUX5_gL21bZxBFef38OFKW73VxehBxM4O'
 . 'k_nWRbGY7ehsMBshXkJQfp97TJ1cV35a9zyAVXC04A';
 
 $token = $config->getParser()->parse($jwt);

Slide 85

Slide 85 text

declare(strict_types=1); use Lcobucci\Clock\SystemClock;
 use Lcobucci\JWT\Validation\Constraint; /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $signer = $config->getSigner(); $key = $config->getVerificationKey();
 $token = $config->getParser()->parse('eyJ0eNiJ9 (...)'); $constraints = [ new Constraint\IssuedBy('https://foo.bar', 'https://bar.foo'), new Constraint\PermittedFor('https://client2.bar'),
 new Constraint\ValidAt(new SystemClock()),
 new Constraint\SignedWith($signer, $key)
 ];
 
 $config->getValidator()->assert($token, ...$constraints);

Slide 86

Slide 86 text

declare(strict_types=1); use Lcobucci\Clock\SystemClock;
 use Lcobucci\JWT\Validation\Constraint; /** @var \Lcobucci\JWT\Configuration $config */ $config = require 'config.php'; $signer = $config->getSigner(); $key = $config->getVerificationKey();
 $token = $config->getParser()->parse('eyJ0eNiJ9 (...)'); $constraints = [ new Constraint\IssuedBy('https://foo.bar', 'https://bar.foo'), new Constraint\PermittedFor('https://client2.bar'),
 new Constraint\ValidAt(new SystemClock()),
 new Constraint\SignedWith($signer, $key)
 ];
 
 var_dump($config->getValidator()->validate($token, ...$constraints));

Slide 87

Slide 87 text

It’s up to you! https://goo.gl/vYG4zt

Slide 88

Slide 88 text

JWT To infinity
 & beyond! authentication Luís Cobucci
 @lcobucci https://goo.gl/gbd3H5

Slide 89

Slide 89 text

Thanks! @lcobucci