2017 © Pedro Peláez
 

silverstripe-vendormodule silverstripe-graphql-jwt

JWT Authentication for GraphQL

image

firesphere/silverstripe-graphql-jwt

JWT Authentication for GraphQL

  • Saturday, December 2, 2017
  • by Firesphere
  • Repository
  • 1 Watchers
  • 0 Stars
  • 1,368 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 2 Forks
  • 4 Open issues
  • 10 Versions
  • 0 % Grown

The README.md

CircleCI Scrutinizer Code Quality codecov, (*1)

License

GPL v3 or later, (*2)

GraphQL JSON Web Token authenticator

This module provides a JWT-interface for creating JSON Web Tokens for authentication., (*3)

Installation

composer require firesphere/graphql-jwt

The default config is available in _config\config.yml., (*4)

In order to securely process and store data via JWT, you need to set a secret key in your .env file:, (*5)

JWT_SIGNER_KEY="[your secret key]"

A quick way to generate a secure random value value for JWT_SIGNER_KEY is through a PHP CLI command:, (*6)

php -r 'echo substr(base64_encode(random_bytes(64)), 0, 64) . "\n";'

You can also use public/private key files., (*7)

JWT_SIGNER_KEY="./path/to/private.key"
JWT_PUBLIC_KEY="./path/to/public.key"

Note: Relative paths will be relative to your BASE_PATH (prefixed with ./), (*8)

Currently, only RSA keys are supported. ECDSA is not supported. The keys in the test-folder are generated by an online RSA key generator., (*9)

The signer key for HMAC can be of any length (keys longer than B bytes are first hashed using H). However, less than L bytes is strongly discouraged as it would decrease the security strength of the function.. Thus, for SHA-256 the signer key should be between 16 and 64 bytes in length., (*10)

The keys in tests/keys should not be trusted!, (*11)

Configuration

Since admin/graphql is reserved exclusively for CMS graphql access, it will be necessary for you to register a custom schema for your front-end application, and apply the provided queries and mutations to that., (*12)

For example, given you've decided to create a schema named frontend at the url /api, (*13)

---
Name: my-graphql-schema
---
SilverStripe\GraphQL\Manager:
  schemas:
    frontend:
      types:
        MemberToken: 'Firesphere\GraphQLJWT\Types\MemberTokenTypeCreator'
        Member: 'Firesphere\GraphQLJWT\Types\MemberTypeCreator'
      mutations:
        createToken: 'Firesphere\GraphQLJWT\Mutations\CreateTokenMutationCreator'
        refreshToken: 'Firesphere\GraphQLJWT\Mutations\RefreshTokenMutationCreator'
      queries:
        validateToken: 'Firesphere\GraphQLJWT\Queries\ValidateTokenQueryCreator'
---
Name: my-graphql-injections
---
SilverStripe\Core\Injector\Injector:
  SilverStripe\GraphQL\Manager.frontend:
    class: SilverStripe\GraphQL\Manager
    constructor:
      identifier: frontend
  SilverStripe\GraphQL\Controller.frontend:
    class: SilverStripe\GraphQL\Controller
    constructor:
      manager: '%$SilverStripe\GraphQL\Manager.frontend'
---
Name: my-graphql-routes
---
SilverStripe\Control\Director:
  rules:
    api:
      Controller: '%$SilverStripe\GraphQL\Controller.frontend'
      Stage: Live

Log in

To generate a JWT token, send a login request to the createToken mutator:, (*14)

mutation {
  createToken(Email: "admin", Password: "password") {
    Token, // ...request or you won't have a token
    ID,
    FirstName,
    Surname
  }
}

Validate token

If you have an app and want to validate your token, you can address the validateToken method:, (*15)

query validateToken {
  validateToken {
    Valid
    Message
    Code
  }
}

It only needs to call the endpoint. The token should be in the header, via your middleware for the request, as a Authorization: Bearer [token]. If the token is valid, you'll get a response like this:, (*16)

{
  "data": {
    "validateToken": {
      "Valid": true,
      "Message": "",
      "Code": 200,
      "__typename": "ValidateToken"
    }
  }
}

If the token is invalid, Valid will be false., (*17)

Anonymous tokens

Although not advised, it's possible to use anonymous tokens. When using an anonymous authenticator, SilverStripe will generate a default database record in the Members table with the Email anonymous and no permissions by default., (*18)

To enable anonymous tokens, add the following to your configuration .yml:, (*19)

SilverStripe\Core\Injector\Injector:
  Firesphere\GraphQLJWT\Mutations\CreateTokenMutationCreator:
    properties:
      CustomAuthenticators:
        - Firesphere\GraphQLJWT\Authentication\AnonymousUserAuthenticator

You can then create an anonymous login with the below query., (*20)

mutation {
  createToken(Email: "anonymous") {
    Token
  }
}

Note: If the default anonymous authenticator doesn't suit your purposes, you can inject any other core SilverStripe authenticator into CustomAuthenticators., (*21)

Warning: The default AnonymousUserAuthenticator is not appropriate for general usage, so don't register this under the core Security class!, (*22)

Enable CORS

To use JWT, CORS needs to be enabled. This can be done by adding the following to your configuration .yml:, (*23)

SilverStripe\GraphQL\Controller:
  cors:
    Enabled: true
    Allow-Origin: "*"
    Allow-Headers: "Authorization, Content-Type"
    Allow-Methods: "GET, POST, OPTIONS"
    Max-Age: 86400 # ...in seconds

Usage

After logging in, you will receive a token which can be used for further requests. This token should be in the header of the request with the Bearer as signature:, (*24)

Authorization: Bearer [token]

Prefix

A prefix can be optionally associated with the unique identifier of a JWT record. This can make it easier to distinguish JWT records created in different contexts, e.g. on a specific domain or environment type. It is not required for security purposes., (*25)

JWT_PREFIX="[your secret prefix]"

Security

Currently, the default method for encrypting the JWT is with SHA256. JWT is signed with multiple factors; including the host, audience (app/remote user), a secret key and a timeframe within which the token is valid. Only one device can be logged in at the time., (*26)

Supported services

By default, JWT only supports login. As it's tokens can not be disabled, nor used for password changes or resets., (*27)

Caveats

When using php under CGI/FastCGI mode with Apache, the Authorization header might not work correctly, see issue#15. The workaround is simple, just add SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0 in your .htaccess file (refer)., (*28)

Examples

A Postman collection can be found in the extra folder., (*29)

Cow?

Of course!, (*30)

               /( ,,,,, )\
              _\,;;;;;;;,/_
           .-"; ;;;;;;;;; ;"-.
           '.__/`_ / \ _`\__.'
              | (')| |(') |
              | .--' '--. |
              |/ o     o \|
              |           |
             / \ _..=.._ / \
            /:. '._____.'   \
           ;::'    / \      .;
           |     _|_ _|_   ::|
         .-|     '==o=='    '|-.
        /  |  . /       \    |  \
        |  | ::|         |   | .|
        |  (  ')         (.  )::|
        |: |   |;  U U  ;|:: | `|
        |' |   | \ U U / |'  |  |
        ##V|   |_/`"""`\_|   |V##
           ##V##         ##V##

The Versions