Quantcast

JWT - JSON Web Tokens - quick'n'dirty

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

JWT - JSON Web Tokens - quick'n'dirty

Alex Betz-3
oops, I just posted this with the wrong subject line. Sorry for the double
posting. A quick and dirty JWT implementation, please share your
improvements!

Best
Alex

<?lassoScript

define jwt_sign(msg, key, method) => {
local(o, method_used = '')
local( methods = map( 'HS256' = 'sha256', 'HS384' = 'sha384', 'HS512' =
'sha512') )
#method_used = #methods->find(#method)

#o = Encrypt_HMAC( -token = #msg,
            -password =  #key,
   -digest = #method_used,
   -Base64
    )

    return(#o)

}


define jwt_decode(jwt, key)  => {
local( o, method, payload, parts, headb64, bodyb64, cryptob64, header,
payload, signature, verified = false )
#parts = #jwt->split('.')
#headb64  = #parts->get(1)
#bodyb64  = #parts->get(2)
#cryptob64  = #parts->get(3)
#header =  json_deserialize(urlsafeB64Decode(#headb64))
#method = #header->find('alg')
#payload =  json_deserialize(urlsafeB64Decode(#bodyb64))
#signature = stringToUrlSafe(jwt_sign(#headb64 + '.' + #bodyb64, #key,
#method))


#verified = (#cryptob64 ==  #signature ? true | false)
if(#verified) => {
#o = #payload
else
#o = 'Signature verification failed'
}

return(#o)
}


define urlsafeB64Decode(input) => {

local(o, temp, padlen, remainder = 0)

#remainder = #input->length % 4

if( #remainder != 0 ) => {
#padlen = 4 - #remainder;
#input += '=' * #padlen
}
#input->replace('-', '+')
#o = bytes(#input)->decodebase64
return(#o)
}


define urlsafeB64Encode(input) => {
local( o, encoded_input, temp1 )
#encoded_input = stringToUrlSafe(string(bytes(#input)->encodebase64))
return(#encoded_input)
}


define jwt_encode(payload, key, method)  => {
local( o, header, headb64, bodyb64, cryptob64 )
#header = map("typ" = "JWT", "alg" = #method)
#headb64 = urlsafeB64Encode(json_serialize(#header))
#bodyb64 = urlsafeB64Encode(json_serialize(#payload))
#cryptob64 = stringToUrlSafe(jwt_sign(#headb64 + '.' + #bodyb64, #key,
#method))

#o = #headb64 + '.' + #bodyb64 + '.' + #cryptob64

return(#o)
}


define stringToUrlSafe(input) => {
local(o)
#o = #input
#o->replace('=', '')
#o->replace('+', '-')
#o->replace('/', '_')
return(#o)
}



local(payload = map('admin' = true, 'name' = 'John Doe', 'sub' =
1234567890) )

jwt_encode(#payload, 'top secret', 'HS256')
'<br>'
jwt_decode('eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0.
eyJhZG1pbiI6IHRydWUsIm5hbWUiOiAiSm9obiBEb2UiLCJzdWIiOiAxMjM0
NTY3ODkwfQ.XUJi-mUasC25uKj0U_mtJrk7gHd7-0OPnfGS-dSdAm8', 'top secret')

?>

#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

Jolle Carlestam-2
21 apr. 2017 kl. 16:44 skrev Alex Betz <[hidden email]>:

>
> oops, I just posted this with the wrong subject line. Sorry for the double
> posting. A quick and dirty JWT implementation, please share your
> improvements!
>
> Best
> Alex
>
> <?lassoScript
>
> define jwt_sign(msg, key, method) => {
> local(o, method_used = '')
> local( methods = map( 'HS256' = 'sha256', 'HS384' = 'sha384', 'HS512' =
> 'sha512') )
> #method_used = #methods->find(#method)
>
> #o = Encrypt_HMAC( -token = #msg,
>            -password =  #key,
>   -digest = #method_used,
>   -Base64
>    )
>
>    return(#o)
>
> }
>

Cool!

I will definitely take a good look. My first finding is the above Encrypt_HMAC snippet. My local machine does not support sha256. (My production server do though). This inhibits me from developing/testing jwt locally.

Instead I’ve spent a good day or so implementing an LJAPI method that uses Java to do the HMAC sha256 encryption/encoding for me. This should make it platform independent.

Does a proper jwt_sign method require support for the other digets methods too? (sha384, sha512)

HDB
Jolle

#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

Alex Betz-3
I believe support for other encryptions is purely optional. It depends a
little bit whether you want to use it for your own purposes or make it a
flexible library. Its probably a good idea to use of some of the 'reserved
claims' (see bellow) part of the library. Most of the front end libraries
us the exp claim to check whether the jwt is 'valid in principal'.

I use JWTs only because I want to connect a lasso driven back end to a
electron application (angular 2 under the hood) and I hit cross site
session management issues. I also want to prepare myself should lasso be
'end of lined'. The sudden move to swift shocked me a bit. I love lasso and
its community to bits (mostly passive) but ...

Just in case you want to use angular 2. Have a look at
https://hackedbychinese.github.io/ng2-idle/. This in combination with JWT
and some sensible server side checking can make up a very powerful session
management without any DB load.

*Reserved claims*: These is a set of predefined claims which are not
mandatory but recommended, to provide a set of useful, interoperable
claims. Some of them are: *iss* (issuer), *exp* (expiration time), *sub*
 (subject), *aud* (audience), and others.

On 21 April 2017 at 17:00, Jolle Carlestam <[hidden email]> wrote:

> 21 apr. 2017 kl. 16:44 skrev Alex Betz <[hidden email]>:
> >
> > oops, I just posted this with the wrong subject line. Sorry for the
> double
> > posting. A quick and dirty JWT implementation, please share your
> > improvements!
> >
> > Best
> > Alex
> >
> > <?lassoScript
> >
> > define jwt_sign(msg, key, method) => {
> > local(o, method_used = '')
> > local( methods = map( 'HS256' = 'sha256', 'HS384' = 'sha384', 'HS512' =
> > 'sha512') )
> > #method_used = #methods->find(#method)
> >
> > #o = Encrypt_HMAC( -token = #msg,
> >            -password =  #key,
> >   -digest = #method_used,
> >   -Base64
> >    )
> >
> >    return(#o)
> >
> > }
> >
>
> Cool!
>
> I will definitely take a good look. My first finding is the above
> Encrypt_HMAC snippet. My local machine does not support sha256. (My
> production server do though). This inhibits me from developing/testing jwt
> locally.
>
> Instead I’ve spent a good day or so implementing an LJAPI method that uses
> Java to do the HMAC sha256 encryption/encoding for me. This should make it
> platform independent.
>
> Does a proper jwt_sign method require support for the other digets methods
> too? (sha384, sha512)
>
> HDB
> Jolle
>
> #############################################################
>
> This message is sent to you because you are subscribed to
>   the mailing list Lasso [hidden email]
> Official list archives available at http://www.lassotalk.com
> To unsubscribe, E-mail to: <[hidden email]>
> Send administrative queries to  <[hidden email]>

#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

Jolle Carlestam-2
OK, I have dipped my toes in this and got some, still wet, results.

First attempt was to rewrite your methods to my liking. They are pasted at the end of this mail.
I do however prefer to use a type for this kind of operation and I have one running. It can be found as a gist here:
https://gist.github.com/jolle-c/bf3ab3ee41bc1abebb0ce3d7b4c69ccd

It still lacks support for public and private keys. I think I need som more insights in how that works before attempting to integrate it. (Bil?)

For the moment it supports 'HS256', ’HS384' and ’HS512’ for signing.
When used on platforms that lacks internal Lasso support for the requested algoritms it will use openssl thru sys_process and shell. Thus shell needs to be installed too.
https://gist.github.com/jolle-c/7e3a6a0d30a032573bb67eae423ff865

It checks validity of the incoming jwt against:
'iss' Issuer.
'sub'  Subject.
'aud' Audience.
'jti' JWT ID.
’exp’ Expiry date time.
’nbf’ Not before date time.
’iat’ Issued at time.

It has support for'verifyat’ allowing you to provide the date time that the validation should use.
And ’gracePeriod’, seconds that the compare date times are allowed to skew from requested time. If not set will default to 0 seconds.

Example usage:

local(
        expirydate = date,
        nbfdate = date,
        sub = lasso_uniqueid
)
#expirydate -> add(-hour = 2)
#nbfdate -> add(-minute = 10)

local(payload = map(
        'admin' = true,
        'name' = 'John Doe',
        'sub' = #sub,
        'exp' = #expirydate -> asinteger,
        'ist' = date -> asinteger,
        'nbf' = #nbfdate -> asinteger,
        'iss' = 'https://mysite.com'
) )

local(encoded = jwt -> encode(#payload, 'top secret', 'HS256'))
#encoded
'<hr />'
local(verifydate = date)
#verifydate -> add(-hour = 1)

local(
        jwt1 = jwt(
                -jwt = #encoded,
                -key = 'top secret',
                -acceptfields = map('iss' = bytes('https://mysite.com'), 'verifyat' = #verifydate)
        )
)
#jwt1 -> verify? #jwt1 -> payload + '<br />' + #jwt1 -> header | #jwt1 -> error
'<hr />'

local(
        jwt2 = jwt(
                -jwt = #encoded,
                -key = 'top secret',
                -acceptfields = map('iss' = bytes('https://othersite.com'), 'verifyat' = #verifydate)
        )
)
#jwt2 -> verify? #jwt2 -> payload + '<br>' + #jwt2 -> header | #jwt2 -> error
'<hr />'
#verifydate -> add(-hour = 3)

local(
        jwt2 = jwt(
                -jwt = #encoded,
                -key = 'top secret',
                -acceptfields = map('iss' = bytes('https://mysite.com'), 'verifyat' = #verifydate)
        )
)
#jwt2 -> verify? #jwt2 -> payload + '<br>' + #jwt2 -> header | #jwt2 -> error


Result:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxNDkzMTA1OTU5LCJpc3MiOiJodHRwczovL215c2l0ZS5jb20iLCJpc3QiOjE0OTMwOTg3NTksIm5hbWUiOiJKb2huIERvZSIsIm5iZiI6MTQ5MzA5OTM1OSwic3ViIjoiQTYyNzE1ODgtMEVCMC00Q0U1LUIxRTItMzkzNkM0MzI1RUE3In0.8pfNZc49pyxDd75E41vmkgJE2BX02C1HJyo35uewZyI
------------
map(admin = true, exp = 1493105959, iss = https://mysite.com, ist = 1493098759, name = John Doe, nbf = 1493099359, sub = A6271588-0EB0-4CE5-B1E2-3936C4325EA7)
map(alg = HS256, typ = JWT)
------------
JWT verify error: Issuer is not allowed (https://mysite.com)
------------
JWT verify error: Token has expired (1493105959)

There’s still things that should be added. As a primary; support for public/private key signing. But it is working and suitable for at least my main needs.

I also have to warn that it is not battle tested as of yet.

Feel free to break and shake to your liking. Error reports, comments and improvements are highly welcome.

HDB
Jolle

ps, the https://hackedbychinese.github.io/ng2-idle/ thing looks really cool and useful. I am fully targeted at Angular 2-4 right now.

ps ps Here’s the tweaked/tampered methods:
define jwt_sign(
        msg::string,
        key::string,
        method::string = 'HS256'
) => {
        local(
                methods = map('HS256' = 'SHA256', 'HS384' = 'SHA384', 'HS512' = 'SHA512'),
                method_used = #methods -> find(#method),
        )

        fail_if(not #method_used), -1, 'Supplied encryption method not supported')

        return encrypt_hmac(
                -token = #msg,
                -password = #key,
                -digest = #method_used,
                -base64
        )

}

define jwt_decode(
        jwt::string,
        key::string,
        allowedmethods::array = array('HS256')
)  => {
        local(
                method,
                payload,
                parts = #jwt -> split('.'),
                partsize = #parts -> size,
                headb64,
                bodyb64,
                cryptob64,
                header,
                signature
        )

        if(#partsize > 1) => {
                #headb64 = #parts -> get(1)
                #bodyb64 = #parts -> get(2)
                #header = json_decode(urlsafeB64Decode(#headb64) -> asstring)
                #method = #header -> find('alg')
                #payload = json_decode(urlsafeB64Decode(#bodyb64) -> asstring)
        }

        if(not (#allowedmethods >> #method)) => {
                stdoutnl('jwt_decode error: Signature verification failed. Signature algoritm not allowed (' + #method + ')')
                return 'Signature verification failed. Signature algoritm not allowed'
        }

        if(#partsize > 2) => {

                #cryptob64 = #parts -> get(3)

                #signature = stringToUrlSafe(jwt_sign(#headb64 + '.' + #bodyb64, #key, #method))
                #cryptob64 ==  #signature ? return #payload
        else(#method == 'none')
                return #payload
        }

        return 'Signature verification failed'

}

define jwt_encode(
        payload,
        key::string,
        method::string
)  => {
        local(
                header = map("typ" = "JWT", "alg" = #method),
                headb64 = urlsafeB64Encode(json_encode(#header)),
                bodyb64 = urlsafeB64Encode(json_encode(#payload)),
                cryptob64 = stringToUrlSafe(jwt_sign(#headb64 + '.' + #bodyb64, #key, #method))
        )
        return #headb64 + '.' + #bodyb64 + '.' + #cryptob64
}

define urlsafeB64Decode(input::string) => {
        local(
                _input = string(#input) // copy as to not tamper with original string
        )
        #_input -> append('=' * ( 4 - #_input -> size % 4))
        #_input -> replace('-', '+')
        return(bytes(#_input)->decodebase64)
}

define urlsafeB64Encode(input::string) => stringToUrlSafe(string(bytes(#input) -> encodebase64))

define stringToUrlSafe(input::string) => {
        local(
                _input = string(#input) // copy as to not tamper with original string
        )
        #_input -> replace('=', '')
        #_input -> replace('+', '-')
        #_input -> replace('/', '_')
        return(#_input)
}



> 22 apr. 2017 kl. 20:10 skrev Alex Betz <[hidden email]>:
>
> I believe support for other encryptions is purely optional. It depends a
> little bit whether you want to use it for your own purposes or make it a
> flexible library. Its probably a good idea to use of some of the 'reserved
> claims' (see bellow) part of the library. Most of the front end libraries
> us the exp claim to check whether the jwt is 'valid in principal'.
>
> I use JWTs only because I want to connect a lasso driven back end to a
> electron application (angular 2 under the hood) and I hit cross site
> session management issues. I also want to prepare myself should lasso be
> 'end of lined'. The sudden move to swift shocked me a bit. I love lasso and
> its community to bits (mostly passive) but ...
>
> Just in case you want to use angular 2. Have a look at
> https://hackedbychinese.github.io/ng2-idle/. This in combination with JWT
> and some sensible server side checking can make up a very powerful session
> management without any DB load.
>
> *Reserved claims*: These is a set of predefined claims which are not
> mandatory but recommended, to provide a set of useful, interoperable
> claims. Some of them are: *iss* (issuer), *exp* (expiration time), *sub*
> (subject), *aud* (audience), and others.


#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

Jolle Carlestam-2
Oh, one more thing.

Regardless if you want to use my code or Alex's there’s a security flaw in the code Alex provided that I think I’ve fixed.
Read more here:
https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/

Basically, you should never use the algoritm provided in the jwt for verifying. Always use one you provide yourself.

HDB
Jolle


> 25 apr. 2017 kl. 10:10 skrev Jolle Carlestam <[hidden email]>:
>
> OK, I have dipped my toes in this and got some, still wet, results.


#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

Alex Betz-3
In reply to this post by Jolle Carlestam-2
Jolle,

I refactored jwt_decode a little bit. A JWT i my view should really have
three part, this simplifies the part checking. instead of error massaging
which might complicate downstream processing, I simply add an error entry
into the payload map. This makes the handling much easier, as you can just
check the payload for an error.

#payload =  jwt_decode(...)

if(#payload->error != 'none') => {

   #payload->error
   abort

}


Best
Alex


define jwt_decode(
jwt::string,
key::string,
allowedmethods::array = array('HS256')
) => {
local( method, payload = map, parts, headb64, bodyb64, cryptob64, header,
signature, error )
#parts = #jwt -> split('.')

if( #parts -> size == 3 ) => {
#headb64 = #parts -> get(1)
#header = json_decode(urlsafeB64Decode(#headb64) -> asstring)
#method = #header -> find('alg')
if(not (#allowedmethods >> #method)) => {
#error = 'Signature verification failed. Signature algoritm not allowed ('
+ #method + ')'

else
#cryptob64 = #parts -> get(3)
#bodyb64 = #parts -> get(2)
#payload = json_decode(urlsafeB64Decode(#bodyb64) -> asstring)
#signature = stringToUrlSafe(jwt_sign(#headb64 + '.' + #bodyb64, #key,
#method))
#error = ( #cryptob64 == #signature ? 'none' | 'tamper alert!' )
}
else
#error = 'Not a valid JWT'
}
#payload -> insert('error' = #error)
return(#payload)
}

On 25 April 2017 at 09:10, Jolle Carlestam <[hidden email]> wrote:

> OK, I have dipped my toes in this and got some, still wet, results.
>
> First attempt was to rewrite your methods to my liking. They are pasted at
> the end of this mail.
> I do however prefer to use a type for this kind of operation and I have
> one running. It can be found as a gist here:
> https://gist.github.com/jolle-c/bf3ab3ee41bc1abebb0ce3d7b4c69ccd
>
> It still lacks support for public and private keys. I think I need som
> more insights in how that works before attempting to integrate it. (Bil?)
>
> For the moment it supports 'HS256', ’HS384' and ’HS512’ for signing.
> When used on platforms that lacks internal Lasso support for the requested
> algoritms it will use openssl thru sys_process and shell. Thus shell needs
> to be installed too.
> https://gist.github.com/jolle-c/7e3a6a0d30a032573bb67eae423ff865
>
> It checks validity of the incoming jwt against:
> 'iss' Issuer.
> 'sub'  Subject.
> 'aud' Audience.
> 'jti' JWT ID.
> ’exp’ Expiry date time.
> ’nbf’ Not before date time.
> ’iat’ Issued at time.
>
> It has support for'verifyat’ allowing you to provide the date time that
> the validation should use.
> And ’gracePeriod’, seconds that the compare date times are allowed to skew
> from requested time. If not set will default to 0 seconds.
>
> Example usage:
>
> local(
>         expirydate      = date,
>         nbfdate         = date,
>         sub             = lasso_uniqueid
> )
> #expirydate -> add(-hour = 2)
> #nbfdate -> add(-minute = 10)
>
> local(payload = map(
>         'admin'         = true,
>         'name'          = 'John Doe',
>         'sub'           = #sub,
>         'exp'           = #expirydate -> asinteger,
>         'ist'           = date -> asinteger,
>         'nbf'           = #nbfdate -> asinteger,
>         'iss'           = 'https://mysite.com'
> ) )
>
> local(encoded = jwt -> encode(#payload, 'top secret', 'HS256'))
> #encoded
> '<hr />'
> local(verifydate = date)
> #verifydate -> add(-hour = 1)
>
> local(
>         jwt1 = jwt(
>                 -jwt    = #encoded,
>                 -key    = 'top secret',
>                 -acceptfields = map('iss' = bytes('https://mysite.com'),
> 'verifyat' = #verifydate)
>         )
> )
> #jwt1 -> verify? #jwt1 -> payload + '<br />' + #jwt1 -> header | #jwt1 ->
> error
> '<hr />'
>
> local(
>         jwt2 = jwt(
>                 -jwt    = #encoded,
>                 -key    = 'top secret',
>                 -acceptfields = map('iss' = bytes('https://othersite.com'),
> 'verifyat' = #verifydate)
>         )
> )
> #jwt2 -> verify? #jwt2 -> payload + '<br>' + #jwt2 -> header | #jwt2 ->
> error
> '<hr />'
> #verifydate -> add(-hour = 3)
>
> local(
>         jwt2 = jwt(
>                 -jwt    = #encoded,
>                 -key    = 'top secret',
>                 -acceptfields = map('iss' = bytes('https://mysite.com'),
> 'verifyat' = #verifydate)
>         )
> )
> #jwt2 -> verify? #jwt2 -> payload + '<br>' + #jwt2 -> header | #jwt2 ->
> error
>
>
> Result:
> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXhwIjoxND
> kzMTA1OTU5LCJpc3MiOiJodHRwczovL215c2l0ZS5jb20iLCJpc3QiOjE0OT
> MwOTg3NTksIm5hbWUiOiJKb2huIERvZSIsIm5iZiI6MTQ5MzA5OTM1OSwic3
> ViIjoiQTYyNzE1ODgtMEVCMC00Q0U1LUIxRTItMzkzNkM0MzI1RUE3In0.
> 8pfNZc49pyxDd75E41vmkgJE2BX02C1HJyo35uewZyI
> ------------
> map(admin = true, exp = 1493105959, iss = https://mysite.com, ist =
> 1493098759, name = John Doe, nbf = 1493099359, sub =
> A6271588-0EB0-4CE5-B1E2-3936C4325EA7)
> map(alg = HS256, typ = JWT)
> ------------
> JWT verify error: Issuer is not allowed (https://mysite.com)
> ------------
> JWT verify error: Token has expired (1493105959)
>
> There’s still things that should be added. As a primary; support for
> public/private key signing. But it is working and suitable for at least my
> main needs.
>
> I also have to warn that it is not battle tested as of yet.
>
> Feel free to break and shake to your liking. Error reports, comments and
> improvements are highly welcome.
>
> HDB
> Jolle
>
> ps, the https://hackedbychinese.github.io/ng2-idle/ thing looks really
> cool and useful. I am fully targeted at Angular 2-4 right now.
>
> ps ps Here’s the tweaked/tampered methods:
> define jwt_sign(
>         msg::string,
>         key::string,
>         method::string  = 'HS256'
> ) => {
>         local(
>                 methods         = map('HS256' = 'SHA256', 'HS384' =
> 'SHA384', 'HS512' = 'SHA512'),
>                 method_used     = #methods -> find(#method),
>         )
>
>         fail_if(not #method_used), -1, 'Supplied encryption method not
> supported')
>
>         return encrypt_hmac(
>                 -token          = #msg,
>                 -password       = #key,
>                 -digest         = #method_used,
>                 -base64
>         )
>
> }
>
> define jwt_decode(
>         jwt::string,
>         key::string,
>         allowedmethods::array   = array('HS256')
> )  => {
>         local(
>                 method,
>                 payload,
>                 parts           = #jwt -> split('.'),
>                 partsize        = #parts -> size,
>                 headb64,
>                 bodyb64,
>                 cryptob64,
>                 header,
>                 signature
>         )
>
>         if(#partsize > 1) => {
>                 #headb64        = #parts -> get(1)
>                 #bodyb64        = #parts -> get(2)
>                 #header         = json_decode(urlsafeB64Decode(#headb64)
> -> asstring)
>                 #method         = #header -> find('alg')
>                 #payload        = json_decode(urlsafeB64Decode(#bodyb64)
> -> asstring)
>         }
>
>         if(not (#allowedmethods >> #method)) => {
>                 stdoutnl('jwt_decode error: Signature verification failed.
> Signature algoritm not allowed (' + #method + ')')
>                 return 'Signature verification failed. Signature algoritm
> not allowed'
>         }
>
>         if(#partsize > 2) => {
>
>                 #cryptob64      = #parts -> get(3)
>
>                 #signature      = stringToUrlSafe(jwt_sign(#headb64 + '.'
> + #bodyb64, #key, #method))
>                 #cryptob64 ==  #signature ? return #payload
>         else(#method == 'none')
>                 return #payload
>         }
>
>         return 'Signature verification failed'
>
> }
>
> define jwt_encode(
>         payload,
>         key::string,
>         method::string
> )  => {
>         local(
>                 header          = map("typ" = "JWT", "alg" = #method),
>                 headb64         = urlsafeB64Encode(json_encode(#header)),
>                 bodyb64         = urlsafeB64Encode(json_encode(#payload)),
>                 cryptob64       = stringToUrlSafe(jwt_sign(#headb64 + '.'
> + #bodyb64, #key, #method))
>         )
>         return #headb64 + '.' + #bodyb64 + '.' + #cryptob64
> }
>
> define urlsafeB64Decode(input::string) => {
>         local(
>                 _input  = string(#input) // copy as to not tamper with
> original string
>         )
>         #_input -> append('=' * ( 4 - #_input -> size % 4))
>         #_input -> replace('-', '+')
>         return(bytes(#_input)->decodebase64)
> }
>
> define urlsafeB64Encode(input::string) => stringToUrlSafe(string(bytes(#input)
> -> encodebase64))
>
> define stringToUrlSafe(input::string) => {
>         local(
>                 _input = string(#input) // copy as to not tamper with
> original string
>         )
>         #_input -> replace('=', '')
>         #_input -> replace('+', '-')
>         #_input -> replace('/', '_')
>         return(#_input)
> }
>
>
>
> > 22 apr. 2017 kl. 20:10 skrev Alex Betz <[hidden email]>:
> >
> > I believe support for other encryptions is purely optional. It depends a
> > little bit whether you want to use it for your own purposes or make it a
> > flexible library. Its probably a good idea to use of some of the
> 'reserved
> > claims' (see bellow) part of the library. Most of the front end libraries
> > us the exp claim to check whether the jwt is 'valid in principal'.
> >
> > I use JWTs only because I want to connect a lasso driven back end to a
> > electron application (angular 2 under the hood) and I hit cross site
> > session management issues. I also want to prepare myself should lasso be
> > 'end of lined'. The sudden move to swift shocked me a bit. I love lasso
> and
> > its community to bits (mostly passive) but ...
> >
> > Just in case you want to use angular 2. Have a look at
> > https://hackedbychinese.github.io/ng2-idle/. This in combination with
> JWT
> > and some sensible server side checking can make up a very powerful
> session
> > management without any DB load.
> >
> > *Reserved claims*: These is a set of predefined claims which are not
> > mandatory but recommended, to provide a set of useful, interoperable
> > claims. Some of them are: *iss* (issuer), *exp* (expiration time), *sub*
> > (subject), *aud* (audience), and others.
>
>
> #############################################################
>
> This message is sent to you because you are subscribed to
>   the mailing list Lasso [hidden email]
> Official list archives available at http://www.lassotalk.com
> To unsubscribe, E-mail to: <[hidden email]>
> Send administrative queries to  <[hidden email]>
>

#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

Alex Betz-3
In reply to this post by Jolle Carlestam-2
I never quite understood this. You use your signature server side to to
simply validate the payload. If the JWT was changed in any way you will get
back gibberish. The payload in itself is not secure in any case, that's why
you use ssl. Where lies the problem? What am I missing? In any case you
dictate the encryption serverside. I cannot se a usecase where you would
not do this. The variation in methods is just to give people using the
'library' the choice of encryption as the server setup might vary.
Alex

On 25 April 2017 at 09:21, Jolle Carlestam <[hidden email]> wrote:

> Oh, one more thing.
>
> Regardless if you want to use my code or Alex's there’s a security flaw in
> the code Alex provided that I think I’ve fixed.
> Read more here:
> https://auth0.com/blog/critical-vulnerabilities-in-
> json-web-token-libraries/
>
> Basically, you should never use the algoritm provided in the jwt for
> verifying. Always use one you provide yourself.
>
> HDB
> Jolle
>
>
> > 25 apr. 2017 kl. 10:10 skrev Jolle Carlestam <[hidden email]>:
> >
> > OK, I have dipped my toes in this and got some, still wet, results.
>
>
> #############################################################
>
> This message is sent to you because you are subscribed to
>   the mailing list Lasso [hidden email]
> Official list archives available at http://www.lassotalk.com
> To unsubscribe, E-mail to: <[hidden email]>
> Send administrative queries to  <[hidden email]>
>

#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

Alex Betz-3
In reply to this post by Alex Betz-3
oops, I coded too much in other languages. What I meant was:

#payload =  jwt_decode(...)

if(#payload->find('error')  != 'none') => {

   #payload->find('error')
   abort

}

Best
Alex

On 25 April 2017 at 11:41, Alex Betz <[hidden email]> wrote:

> Jolle,
>
> I refactored jwt_decode a little bit. A JWT i my view should really have
> three part, this simplifies the part checking. instead of error massaging
> which might complicate downstream processing, I simply add an error entry
> into the payload map. This makes the handling much easier, as you can just
> check the payload for an error.
>
> #payload =  jwt_decode(...)
>
> if(#payload->error != 'none') => {
>
>    #payload->error
>    abort
>
> }
>
>
> Best
> Alex
>
>
> define jwt_decode(
> jwt::string,
> key::string,
> allowedmethods::array = array('HS256')
> ) => {
> local( method, payload = map, parts, headb64, bodyb64, cryptob64, header,
> signature, error )
> #parts = #jwt -> split('.')
>
> if( #parts -> size == 3 ) => {
> #headb64 = #parts -> get(1)
> #header = json_decode(urlsafeB64Decode(#headb64) -> asstring)
> #method = #header -> find('alg')
> if(not (#allowedmethods >> #method)) => {
> #error = 'Signature verification failed. Signature algoritm not allowed ('
> + #method + ')'
>
> else
> #cryptob64 = #parts -> get(3)
> #bodyb64 = #parts -> get(2)
> #payload = json_decode(urlsafeB64Decode(#bodyb64) -> asstring)
> #signature = stringToUrlSafe(jwt_sign(#headb64 + '.' + #bodyb64, #key,
> #method))
> #error = ( #cryptob64 == #signature ? 'none' | 'tamper alert!' )
> }
> else
> #error = 'Not a valid JWT'
> }
> #payload -> insert('error' = #error)
> return(#payload)
> }
>
> On 25 April 2017 at 09:10, Jolle Carlestam <[hidden email]> wrote:
>
>> OK, I have dipped my toes in this and got some, still wet, results.
>>
>> First attempt was to rewrite your methods to my liking. They are pasted
>> at the end of this mail.
>> I do however prefer to use a type for this kind of operation and I have
>> one running. It can be found as a gist here:
>> https://gist.github.com/jolle-c/bf3ab3ee41bc1abebb0ce3d7b4c69ccd
>>
>> It still lacks support for public and private keys. I think I need som
>> more insights in how that works before attempting to integrate it. (Bil?)
>>
>> For the moment it supports 'HS256', ’HS384' and ’HS512’ for signing.
>> When used on platforms that lacks internal Lasso support for the
>> requested algoritms it will use openssl thru sys_process and shell. Thus
>> shell needs to be installed too.
>> https://gist.github.com/jolle-c/7e3a6a0d30a032573bb67eae423ff865
>>
>> It checks validity of the incoming jwt against:
>> 'iss' Issuer.
>> 'sub'  Subject.
>> 'aud' Audience.
>> 'jti' JWT ID.
>> ’exp’ Expiry date time.
>> ’nbf’ Not before date time.
>> ’iat’ Issued at time.
>>
>> It has support for'verifyat’ allowing you to provide the date time that
>> the validation should use.
>> And ’gracePeriod’, seconds that the compare date times are allowed to
>> skew from requested time. If not set will default to 0 seconds.
>>
>> Example usage:
>>
>> local(
>>         expirydate      = date,
>>         nbfdate         = date,
>>         sub             = lasso_uniqueid
>> )
>> #expirydate -> add(-hour = 2)
>> #nbfdate -> add(-minute = 10)
>>
>> local(payload = map(
>>         'admin'         = true,
>>         'name'          = 'John Doe',
>>         'sub'           = #sub,
>>         'exp'           = #expirydate -> asinteger,
>>         'ist'           = date -> asinteger,
>>         'nbf'           = #nbfdate -> asinteger,
>>         'iss'           = 'https://mysite.com'
>> ) )
>>
>> local(encoded = jwt -> encode(#payload, 'top secret', 'HS256'))
>> #encoded
>> '<hr />'
>> local(verifydate = date)
>> #verifydate -> add(-hour = 1)
>>
>> local(
>>         jwt1 = jwt(
>>                 -jwt    = #encoded,
>>                 -key    = 'top secret',
>>                 -acceptfields = map('iss' = bytes('https://mysite.com'),
>> 'verifyat' = #verifydate)
>>         )
>> )
>> #jwt1 -> verify? #jwt1 -> payload + '<br />' + #jwt1 -> header | #jwt1 ->
>> error
>> '<hr />'
>>
>> local(
>>         jwt2 = jwt(
>>                 -jwt    = #encoded,
>>                 -key    = 'top secret',
>>                 -acceptfields = map('iss' = bytes('https://othersite.com
>> '), 'verifyat' = #verifydate)
>>         )
>> )
>> #jwt2 -> verify? #jwt2 -> payload + '<br>' + #jwt2 -> header | #jwt2 ->
>> error
>> '<hr />'
>> #verifydate -> add(-hour = 3)
>>
>> local(
>>         jwt2 = jwt(
>>                 -jwt    = #encoded,
>>                 -key    = 'top secret',
>>                 -acceptfields = map('iss' = bytes('https://mysite.com'),
>> 'verifyat' = #verifydate)
>>         )
>> )
>> #jwt2 -> verify? #jwt2 -> payload + '<br>' + #jwt2 -> header | #jwt2 ->
>> error
>>
>>
>> Result:
>> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwiZXh
>> wIjoxNDkzMTA1OTU5LCJpc3MiOiJodHRwczovL215c2l0ZS5jb20iLCJpc3Q
>> iOjE0OTMwOTg3NTksIm5hbWUiOiJKb2huIERvZSIsIm5iZiI6MTQ5MzA5OTM
>> 1OSwic3ViIjoiQTYyNzE1ODgtMEVCMC00Q0U1LUIxRTItMzkzNkM0MzI1RUE
>> 3In0.8pfNZc49pyxDd75E41vmkgJE2BX02C1HJyo35uewZyI
>> ------------
>> map(admin = true, exp = 1493105959, iss = https://mysite.com, ist =
>> 1493098759, name = John Doe, nbf = 1493099359, sub =
>> A6271588-0EB0-4CE5-B1E2-3936C4325EA7)
>> map(alg = HS256, typ = JWT)
>> ------------
>> JWT verify error: Issuer is not allowed (https://mysite.com)
>> ------------
>> JWT verify error: Token has expired (1493105959)
>>
>> There’s still things that should be added. As a primary; support for
>> public/private key signing. But it is working and suitable for at least my
>> main needs.
>>
>> I also have to warn that it is not battle tested as of yet.
>>
>> Feel free to break and shake to your liking. Error reports, comments and
>> improvements are highly welcome.
>>
>> HDB
>> Jolle
>>
>> ps, the https://hackedbychinese.github.io/ng2-idle/ thing looks really
>> cool and useful. I am fully targeted at Angular 2-4 right now.
>>
>> ps ps Here’s the tweaked/tampered methods:
>> define jwt_sign(
>>         msg::string,
>>         key::string,
>>         method::string  = 'HS256'
>> ) => {
>>         local(
>>                 methods         = map('HS256' = 'SHA256', 'HS384' =
>> 'SHA384', 'HS512' = 'SHA512'),
>>                 method_used     = #methods -> find(#method),
>>         )
>>
>>         fail_if(not #method_used), -1, 'Supplied encryption method not
>> supported')
>>
>>         return encrypt_hmac(
>>                 -token          = #msg,
>>                 -password       = #key,
>>                 -digest         = #method_used,
>>                 -base64
>>         )
>>
>> }
>>
>> define jwt_decode(
>>         jwt::string,
>>         key::string,
>>         allowedmethods::array   = array('HS256')
>> )  => {
>>         local(
>>                 method,
>>                 payload,
>>                 parts           = #jwt -> split('.'),
>>                 partsize        = #parts -> size,
>>                 headb64,
>>                 bodyb64,
>>                 cryptob64,
>>                 header,
>>                 signature
>>         )
>>
>>         if(#partsize > 1) => {
>>                 #headb64        = #parts -> get(1)
>>                 #bodyb64        = #parts -> get(2)
>>                 #header         = json_decode(urlsafeB64Decode(#headb64)
>> -> asstring)
>>                 #method         = #header -> find('alg')
>>                 #payload        = json_decode(urlsafeB64Decode(#bodyb64)
>> -> asstring)
>>         }
>>
>>         if(not (#allowedmethods >> #method)) => {
>>                 stdoutnl('jwt_decode error: Signature verification
>> failed. Signature algoritm not allowed (' + #method + ')')
>>                 return 'Signature verification failed. Signature algoritm
>> not allowed'
>>         }
>>
>>         if(#partsize > 2) => {
>>
>>                 #cryptob64      = #parts -> get(3)
>>
>>                 #signature      = stringToUrlSafe(jwt_sign(#headb64 +
>> '.' + #bodyb64, #key, #method))
>>                 #cryptob64 ==  #signature ? return #payload
>>         else(#method == 'none')
>>                 return #payload
>>         }
>>
>>         return 'Signature verification failed'
>>
>> }
>>
>> define jwt_encode(
>>         payload,
>>         key::string,
>>         method::string
>> )  => {
>>         local(
>>                 header          = map("typ" = "JWT", "alg" = #method),
>>                 headb64         = urlsafeB64Encode(json_encode(#header)),
>>                 bodyb64         = urlsafeB64Encode(json_encode(#
>> payload)),
>>                 cryptob64       = stringToUrlSafe(jwt_sign(#headb64 +
>> '.' + #bodyb64, #key, #method))
>>         )
>>         return #headb64 + '.' + #bodyb64 + '.' + #cryptob64
>> }
>>
>> define urlsafeB64Decode(input::string) => {
>>         local(
>>                 _input  = string(#input) // copy as to not tamper with
>> original string
>>         )
>>         #_input -> append('=' * ( 4 - #_input -> size % 4))
>>         #_input -> replace('-', '+')
>>         return(bytes(#_input)->decodebase64)
>> }
>>
>> define urlsafeB64Encode(input::string) => stringToUrlSafe(string(bytes(#input)
>> -> encodebase64))
>>
>> define stringToUrlSafe(input::string) => {
>>         local(
>>                 _input = string(#input) // copy as to not tamper with
>> original string
>>         )
>>         #_input -> replace('=', '')
>>         #_input -> replace('+', '-')
>>         #_input -> replace('/', '_')
>>         return(#_input)
>> }
>>
>>
>>
>> > 22 apr. 2017 kl. 20:10 skrev Alex Betz <[hidden email]>:
>> >
>> > I believe support for other encryptions is purely optional. It depends a
>> > little bit whether you want to use it for your own purposes or make it a
>> > flexible library. Its probably a good idea to use of some of the
>> 'reserved
>> > claims' (see bellow) part of the library. Most of the front end
>> libraries
>> > us the exp claim to check whether the jwt is 'valid in principal'.
>> >
>> > I use JWTs only because I want to connect a lasso driven back end to a
>> > electron application (angular 2 under the hood) and I hit cross site
>> > session management issues. I also want to prepare myself should lasso be
>> > 'end of lined'. The sudden move to swift shocked me a bit. I love lasso
>> and
>> > its community to bits (mostly passive) but ...
>> >
>> > Just in case you want to use angular 2. Have a look at
>> > https://hackedbychinese.github.io/ng2-idle/. This in combination with
>> JWT
>> > and some sensible server side checking can make up a very powerful
>> session
>> > management without any DB load.
>> >
>> > *Reserved claims*: These is a set of predefined claims which are not
>> > mandatory but recommended, to provide a set of useful, interoperable
>> > claims. Some of them are: *iss* (issuer), *exp* (expiration time), *sub*
>> > (subject), *aud* (audience), and others.
>>
>>
>> #############################################################
>>
>> This message is sent to you because you are subscribed to
>>   the mailing list Lasso [hidden email]
>> Official list archives available at http://www.lassotalk.com
>> To unsubscribe, E-mail to: <[hidden email]>
>> Send administrative queries to  <[hidden email]>
>>
>
>

#############################################################

This message is sent to you because you are subscribed to
  the mailing list Lasso [hidden email]
Official list archives available at http://www.lassotalk.com
To unsubscribe, E-mail to: <[hidden email]>
Send administrative queries to  <[hidden email]>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: JWT - JSON Web Tokens - quick'n'dirty

kimv
This post has NOT been accepted by the mailing list yet.
Here's more information on JWT...

Things to Use Instead of JWT | Kevin Burke
https://kev.inburke.com/kevin/things-to-use-instead-of-jwt/
Loading...