Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions _config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,3 @@ SilverStripe\Core\Injector\Injector:
Firesphere\GraphQLJWT\Authentication\JWTAuthenticationHandler:
properties:
JWTAuthenticator: '%$Firesphere\GraphQLJWT\Authentication\JWTAuthenticator'
Firesphere\GraphQLJWT\Mutations\CreateTokenMutationCreator:
properties:
JWTAuthenticator: '%$Firesphere\GraphQLJWT\Authentication\JWTAuthenticator'
Firesphere\GraphQLJWT\Mutations\RefreshTokenMutationCreator:
properties:
JWTAuthenticator: '%$Firesphere\GraphQLJWT\Authentication\JWTAuthenticator'
Firesphere\GraphQLJWT\Queries\ValidateTokenQueryCreator:
properties:
JWTAuthenticator: '%$Firesphere\GraphQLJWT\Authentication\JWTAuthenticator'
2 changes: 2 additions & 0 deletions _graphql/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resolvers:
- Firesphere\GraphQLJWT\Resolvers\Resolver
17 changes: 17 additions & 0 deletions _graphql/enums.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
TokenStatus:
values:
OK:
value: OK
description: JWT token is valid
INVALID:
value: INVALID
description: JWT token is valid
EXPIRED:
value: EXPIRED
description: JWT token has expired, but can be renewed
DEAD:
value: DEAD
description: JWT token has expired and cannot be renewed
BAD_LOGIN:
value: BAD_LOGIN
description: JWT token could not be created due to invalid login credentials
6 changes: 6 additions & 0 deletions _graphql/models.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SilverStripe\Security\Member:
fields:
id: true
firstName: true
surname: true
email: true
6 changes: 6 additions & 0 deletions _graphql/mutations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
refreshToken:
type: MemberToken
description: Refreshes a JWT token for a valid user. To be done
'createToken(email: String!, password: String)':
type: MemberToken
description: Creates a JWT token for a valid user
3 changes: 3 additions & 0 deletions _graphql/queries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
validateToken:
description: Validates a given token from the Bearer header
type: MemberToken
9 changes: 9 additions & 0 deletions _graphql/types.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MemberToken:
fields:
valid: Boolean
member:
model: SilverStripe\Security\Member
token: String
status: TokenStatus
code: Int
message: String
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"require": {
"php": ">=7.1",
"silverstripe/framework": "^4.3",
"silverstripe/graphql": "^3.0",
"silverstripe/graphql": "4.x-dev",
"lcobucci/jwt": "^3.2",
"ext-json": "*"
},
Expand Down
73 changes: 26 additions & 47 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,43 +43,20 @@ The signer key [for HMAC can be of any length (keys longer than B bytes are firs
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.

For example, given you've decided to create a schema named `frontend` at the url `/api`
For example, given you've decided to create a schema named `default` at the url `/graphql`
(which is provided by the core recipe out of the box), all you need to do is
add the `firesphere/graphql-jwt` config to your schema.

```yml
---
Name: my-graphql-schema
---
SilverStripe\GraphQL\Manager:
SilverStripe\GraphQL\Schema\Schema:
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:
default:
src:
- 'firesphere/graphql-jwt: _graphql'
Controller: '%$SilverStripe\GraphQL\Controller.frontend'
Stage: Live
```


Expand All @@ -89,11 +66,13 @@ To generate a JWT token, send a login request to the `createToken` mutator:

```graphql
mutation {
createToken(Email: "admin", Password: "password") {
Token, // ...request or you won't have a token
ID,
FirstName,
Surname
createToken(email: "admin", password: "password") {
token // ...request or you won't have a token
id
member {
firstName
surname
}
}
}
```
Expand All @@ -105,9 +84,9 @@ If you have an app and want to validate your token, you can address the `validat
```graphql
query validateToken {
validateToken {
Valid
Message
Code
valid
message
code
}
}
```
Expand All @@ -118,16 +97,16 @@ It only needs to call the endpoint. The token should be in the header, via your
{
"data": {
"validateToken": {
"Valid": true,
"Message": "",
"Code": 200,
"valid": true,
"message": "",
"code": 200,
"__typename": "ValidateToken"
}
}
}
```

If the token is invalid, `Valid` will be `false`.
If the token is invalid, `valid` will be `false`.

## Anonymous tokens

Expand All @@ -138,7 +117,7 @@ To enable anonymous tokens, add the following to your configuration `.yml`:

```yaml
SilverStripe\Core\Injector\Injector:
Firesphere\GraphQLJWT\Mutations\CreateTokenMutationCreator:
Firesphere\GraphQLJWT\Authentication\CustomAuthenticatorRegistry:
properties:
CustomAuthenticators:
- Firesphere\GraphQLJWT\Authentication\AnonymousUserAuthenticator
Expand All @@ -148,14 +127,14 @@ You can then create an anonymous login with the below query.

```graphql
mutation {
createToken(Email: "anonymous") {
Token
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`.
core Silverstripe CMS authenticator into `CustomAuthenticators`.

Warning: The default `AnonymousUserAuthenticator` is not appropriate for general usage, so don't
register this under the core `Security` class!
Expand Down Expand Up @@ -188,7 +167,7 @@ Currently, the default method for encrypting the JWT is with SHA256. JWT is sign

## Supported services

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

## Caveats

Expand Down
40 changes: 40 additions & 0 deletions src/Authentication/CustomAuthenticatorRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php


namespace Firesphere\GraphQLJWT\Authentication;


use Firesphere\GraphQLJWT\Mutations\CreateTokenMutationCreator;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Security\Authenticator;

class CustomAuthenticatorRegistry
{
use Injectable;

/**
* Extra authenticators to use for logging in with username / password
*
* @var Authenticator[]
*/
protected $customAuthenticators = [];

/**
* @return Authenticator[]
*/
public function getCustomAuthenticators(): array
{
return $this->customAuthenticators;
}

/**
* @param Authenticator[] $authenticators
* @return $this
*/
public function setCustomAuthenticators(array $authenticators): self
{
$this->customAuthenticators = $authenticators;
return $this;
}

}
6 changes: 2 additions & 4 deletions src/Authentication/JWTAuthenticationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
use Exception;
use Firesphere\GraphQLJWT\Extensions\MemberExtension;
use Firesphere\GraphQLJWT\Helpers\HeaderExtractor;
use Firesphere\GraphQLJWT\Helpers\RequiresAuthenticator;
use OutOfBoundsException;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Security\AuthenticationHandler;
use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
Expand All @@ -22,7 +22,6 @@
class JWTAuthenticationHandler implements AuthenticationHandler
{
use HeaderExtractor;
use RequiresAuthenticator;
use Injectable;

/**
Expand All @@ -41,8 +40,7 @@ public function authenticateRequest(HTTPRequest $request): ?Member
}

// Validate the token. This is critical for security
$member = $this
->getJWTAuthenticator()
$member = Injector::inst()->get(JWTAuthenticator::class)
->authenticate(['token' => $token], $request);

if ($member) {
Expand Down
Loading