eXistSolutions / exist-jwt

create, sign, read and verify JSON Web Tokens in existdb
MIT License
8 stars 1 forks source link

Document approaches to secrets #13

Open ahenket opened 3 years ago

ahenket commented 3 years ago

When you write code against the jwt:instance() function, you need to supply a secret. This secret contributes in the security of the system. This requires a few things:

One of the approaches is the one I implemented. This is not because it is 'the' approach, but at least a sample approach for people looking into a relatively simple way.

We have an API that is installed through the regular xar mechanism. In the post-install.xql I have added a function that adds resource /db/secure/security.xml if it does not exist yet. This file contains (at least) the maximum token lifetime and the secret (currently uuid but could be anything). The token code reads this security.xml for these two items and just errors if it can't (on purpose).

post-install.xql:

let $strSecureConfig := '/db/secure'

let $createCollection :=
  if (xmldb:collection-available($strSecureConfig)) then () else 
    xmldb:create-collection('/db', substring-after($strSecureConfig, '/db/')
  )
let $updatePermissions  := (
  sm:chown(xs:anyURI($strSecureConfig), '...api-user'),
  sm:chgrp(xs:anyURI($strSecureConfig), 'dba'),
  sm:chmod(xs:anyURI($strSecureConfig), 'r-xrws---'),
  sm:clear-acl(xs:anyURI($strSecureConfig))
)
let $createResource := 
  if (doc-available($strSecureConfig || '/security.xml')) then (
    $strSecureConfig || '/security.xml'
  ) else (
    xmldb:store($strSecureConfig, 'security.xml', 
      document {
        comment { ' This resource supports the API login process based on JWT tokens. ' } |
          <authentication>
            <!-- how will the token be valid in seconds: 30 minutes = 30*60 seconds, 
                    1 hour = 60*60 seconds, 4 hours 4*60*60 seconds, 8 hours = 8*60*60 -->
            <token-lifetime>{8*60*60}</token-lifetime>
            <!-- user in token generation and validation, this should be unique on every 
                    server to avoid token reuse across servers. You may change this value 
                    to anything later on. This renders tokens based on the previous value invalid -->
            <secret>{util:uuid()}</secret>
          </authentication>
        }
      )
    )
let $updatePermissions  := (
  sm:chown(xs:anyURI($createResource), '...api-user'),
  sm:chgrp(xs:anyURI($createResource), 'dba'),
  sm:chmod(xs:anyURI($createResource), 'r-xrws---'),
  sm:clear-acl(xs:anyURI($createResource))
)

jat-auth code fragment:

(:~ configure and get JWT instance :)
declare %private variable $jwt-auth:secret := doc($strSecurityResource)/authentication/secret;
declare %private variable $jwt-auth:token-lifetime := doc($strSecurityResource)/authentication/token-lifetime;
declare %private variable $jwt-auth:jwt := jwt:instance($jwt-auth:secret, $jwt-auth:token-lifetime);
line-o commented 3 years ago

Thanks a ton @ahenket for sharing. We should definitely add this to the documentation.