Skip to content

Commit 054d6b7

Browse files
committed
- Adding Tests
1 parent 420bc42 commit 054d6b7

9 files changed

+297
-19
lines changed

src/Auth.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static function get_secret_key() {
2424

2525
// Use the defined secret key, if it exists, otherwise use the SECURE_AUTH_SALT if it exists
2626
// @see: https://api.wordpress.org/secret-key/1.1/salt/
27-
$salt = defined( SECURE_AUTH_SALT ) ? SECURE_AUTH_SALT : null;
27+
$salt = defined( SECURE_AUTH_SALT ) ? SECURE_AUTH_SALT : 'jwt-authentication';
2828
$secret_key = defined( 'GRAPHQL_JWT_AUTH_SECRET_KEY' ) ? GRAPHQL_JWT_AUTH_SECRET_KEY : $salt;
2929

3030
return apply_filters( 'graphql_jwt_auth_secret_key', $secret_key );
@@ -107,7 +107,7 @@ public static function get_token_expiration() {
107107
/**
108108
* Set the expiration time, default is 300 seconds.
109109
*/
110-
$expiration = self::get_token_issued() + ( 300 );
110+
$expiration = self::get_token_issued() + 300;
111111

112112
/**
113113
* Determine the expiration value. Default is 7 days, but is filterable to be configured as needed
@@ -118,7 +118,7 @@ public static function get_token_expiration() {
118118

119119
}
120120

121-
return ! empty( self::$expiration ) && is_string( self::$expiration ) ? self::$expiration : null;
121+
return ! empty( self::$expiration ) ? self::$expiration : null;
122122

123123
}
124124

@@ -484,6 +484,12 @@ public static function unrevoke_user_secret( int $user_id ) {
484484

485485
}
486486

487+
protected static function set_status( $status_code ) {
488+
add_filter( 'graphql_response_status_code', function() use ( $status_code ) {
489+
return $status_code;
490+
});
491+
}
492+
487493
/**
488494
* Main validation function, this function try to get the Authentication
489495
* headers and decoded.
@@ -575,6 +581,7 @@ public static function validate_token( $token = null, $refresh = false ) {
575581
* If any exceptions are caught
576582
*/
577583
} catch ( \Exception $error ) {
584+
self::set_status( 403 );
578585
return new \WP_Error( 'invalid_token', __( 'The JWT Token is invalid', 'wp-graphql-jwt-authentication' ) );
579586
}
580587

src/ManageTokens.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public static function add_tokens_to_graphql_response_headers( $headers ) {
275275
* If the tokens can be generated (not revoked, etc), return them
276276
*/
277277
if ( ! empty( $auth_token ) && ! is_wp_error( $auth_token ) ) {
278-
$headers['X-jwt-auth-token'] = $auth_token;
278+
$headers['X-JWT-Auth'] = $auth_token;
279279
}
280280

281281
}
@@ -287,7 +287,7 @@ public static function add_tokens_to_graphql_response_headers( $headers ) {
287287
$refresh_token = Auth::get_refresh_token( new \WP_User( $validate_auth_header->data->user->id ), false );
288288

289289
if ( ! empty( $refresh_token ) && ! is_wp_error( $refresh_token ) ) {
290-
$headers['X-jwt-refresh-token'] = $refresh_token;
290+
$headers['X-JWT-Refresh'] = $refresh_token;
291291
}
292292

293293
}

tests/_data/config.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
* fatal errors when the autoloader is loaded twice
66
*/
77
define( 'WPGRAPHQL_JWT_AUTHENTICATION_AUTOLOAD', false );
8+
define( 'GRAPHQL_JWT_AUTH_SECRET_KEY', 'codeception_tests' );

tests/functional.suite.dist.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ modules:
2424
WPDb:
2525
dsn: 'mysql:host=127.0.0.1;dbname=wpgraphql_jwt_auth_serve'
2626
user: 'root'
27-
password: 'jbutt#123'
27+
password: ''
2828
dump: 'tests/_data/dump.sql'
2929
populate: true
3030
cleanup: true
@@ -37,5 +37,5 @@ modules:
3737
dbName: wpgraphql_jwt_auth_serve
3838
dbHost: 127.0.0.1
3939
dbUser: root
40-
dbPassword: jbutt#123
40+
dbPassword: ''
4141
configFile: "tests/_data/config.php"

tests/functional/AuthCept.php

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
11
<?php
2-
32
$I = new FunctionalTester($scenario);
4-
$I->wantTo('Get Posts with GraphQL');
5-
$I->sendPost( 'https://denverpost.com/graphql', json_encode([
6-
'query' => '{ posts{ edges{ node{ id, title } } } }'
3+
$I->wantTo('Get public data without passing authentication headers');
4+
5+
$I->sendPOST( 'http://wp.localhost/graphql', json_encode([
6+
'query' => '
7+
{
8+
posts {
9+
edges {
10+
node {
11+
id
12+
title
13+
link
14+
date
15+
}
16+
}
17+
}
18+
}'
719
]) );
8-
$I->seeResponseCodeIs( 403 );
9-
$I->seeResponseIsJson();
1020

21+
$I->seeResponseCodeIs( 200 );
22+
$I->seeResponseIsJson();
23+
$response = $I->grabResponse();
1124

25+
$response_array = json_decode( $response, true );
1226

13-
$res = $I->sendPOST( 'http://wp.localhost/graphql', json_encode([
14-
'query' => '{ posts{ edges{ node{ id, title } } } }'
15-
]) );
16-
$I->seeResponseCodeIs( 200 );
17-
$I->seeResponseIsJson();
27+
$I->assertArrayNotHasKey( 'errors', $response_array );
28+
$I->assertArrayHasKey( 'data', $response_array );
29+
$I->assertNotEmpty( $response_array['data']['posts']['edges'][0]['node']['id'] );
30+
$I->assertNotEmpty( $response_array['data']['posts']['edges'][0]['node']['title'] );
31+
$I->assertNotEmpty( $response_array['data']['posts']['edges'][0]['node']['link'] );
32+
$I->assertNotEmpty( $response_array['data']['posts']['edges'][0]['node']['date'] );
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
$I = new FunctionalTester($scenario);
3+
$I->wantTo('Make an authenticated request to generate a Refresh token');
4+
5+
$username = uniqid();
6+
$user = $I->haveUserInDatabase( $username, 'administrator', [ 'user_pass' => 'password' ] );
7+
8+
/**
9+
* Login with username and password to get the authToken for use in the subsequent Authenticated request
10+
*/
11+
$I->sendPOST( 'http://wp.localhost/graphql', json_encode([
12+
'query' => '
13+
mutation Login($input: LoginInput!) {
14+
login( input: $input ) {
15+
authToken
16+
refreshToken
17+
user {
18+
username
19+
}
20+
}
21+
}
22+
',
23+
'variables' => [
24+
'input' => [
25+
'username' => $username,
26+
'password' => 'password',
27+
'clientMutationId' => uniqid(),
28+
]
29+
],
30+
], true));
31+
32+
$I->seeResponseCodeIs( 200 );
33+
$I->seeResponseIsJson();
34+
35+
$response = $I->grabResponse();
36+
$response_array = json_decode( $response, true );
37+
$I->assertArrayNotHasKey( 'errors', $response_array );
38+
$I->assertArrayHasKey( 'data', $response_array );
39+
40+
$authToken = $response_array['data']['login']['authToken'];
41+
$refreshToken = $response_array['data']['login']['refreshToken'];
42+
43+
/**
44+
* Set the Authorization header using the authToken retrieved in the previous request.
45+
* The authToken can be used to access resources (or mutate data) on behalf of the user it was issued for.
46+
* Here we will make a request to get data about the user, using the authToken as the mechanism for setting
47+
* the current user.
48+
*/
49+
$I->setHeader( 'Authorization', 'Bearer ' . $authToken );
50+
$I->sendPOST( 'http://wp.localhost/graphql', json_encode([
51+
'query' => '
52+
{
53+
viewer {
54+
username
55+
jwtAuthToken
56+
jwtUserSecret
57+
jwtRefreshToken
58+
jwtAuthExpiration
59+
isJwtAuthSecretRevoked
60+
}
61+
}
62+
',
63+
], true));
64+
65+
/**
66+
* The repsonse code should be 200
67+
*/
68+
$I->seeResponseCodeIs( 200 );
69+
70+
/**
71+
* Grab the Refresh header. Because the request was properly authenticated, there should
72+
* be a valid refresh header in the response
73+
*/
74+
$refreshTokenHeader = $I->grabHttpHeader('X-JWT-Refresh' );
75+
$I->assertNotEmpty( $refreshTokenHeader );
76+
77+
/**
78+
* The response should be JSON
79+
*/
80+
$I->seeResponseIsJson();
81+
82+
/**
83+
* Get the JSON response
84+
*/
85+
$response = $I->grabResponse();
86+
87+
/**
88+
* Convert the response to JSON for making assertions
89+
*/
90+
$response_array = json_decode( $response, true );
91+
92+
/**
93+
* The request should be valid, so we expect no errors
94+
*/
95+
$I->assertArrayNotHasKey( 'errors', $response_array );
96+
97+
/**
98+
* A valid request should contain the data in the response
99+
*/
100+
$I->assertArrayHasKey( 'data', $response_array );
101+
102+
/**
103+
* The username of the viewer should match the username for the Token we retrieved and sent a request with
104+
*/
105+
$I->assertEquals( $username, $response_array['data']['viewer']['username'] );
106+
107+
/**
108+
* The request should provide a new jwtAuthToken that we can use for future requests
109+
*/
110+
$I->assertNotEmpty( $response_array['data']['viewer']['jwtAuthToken'] );
111+
112+
/**
113+
* The request should provide the secret for the user, because the user has access to see their own JWT secret
114+
*/
115+
$I->assertNotEmpty( $response_array['data']['viewer']['jwtUserSecret'] );
116+
117+
/**
118+
* The request should provide a new JWT Refresh Token that can be used for future requests to get a new AccessToken
119+
*/
120+
$I->assertNotEmpty( $response_array['data']['viewer']['jwtRefreshToken'] );
121+
122+
/**
123+
* The request should provide info on the auth expiration for the user. This field is useful for building an interface
124+
* where the user can see how long their expiration is and can then mutate the expiration timeframe, should there be
125+
* per-user customization of expiration settings.
126+
*/
127+
$I->assertNotEmpty( $response_array['data']['viewer']['jwtAuthExpiration'] );
128+
129+
/**
130+
* The JWT should not be revoked for this user, so this assertion should be false
131+
*/
132+
$I->assertFalse( $response_array['data']['viewer']['isJwtAuthSecretRevoked'] );
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
$I = new FunctionalTester($scenario);
3+
$I->wantTo('Make an invalid authenticated request and verify I get a 403 response and cannot access private data, but can still access public data');
4+
5+
$invalidAuthToken = 'invalidAuthToken';
6+
7+
/**
8+
* Set the Authorization token with an invalid token, and try to request private data.
9+
*
10+
* This should return a 403, and should not return any private data for the user, but should still return public data
11+
*/
12+
$I->setHeader( 'Authorization', 'Bearer ' . $invalidAuthToken );
13+
$I->sendPOST( 'http://wp.localhost/graphql', json_encode([
14+
'query' => '
15+
{
16+
posts(first: 1) {
17+
edges {
18+
node {
19+
id
20+
title
21+
}
22+
}
23+
}
24+
viewer {
25+
username
26+
jwtAuthToken
27+
jwtUserSecret
28+
jwtRefreshToken
29+
jwtAuthExpiration
30+
isJwtAuthSecretRevoked
31+
}
32+
}
33+
',
34+
], true));
35+
36+
/**
37+
* The repsonse code should be 403, because auth was invalid
38+
*/
39+
$I->seeResponseCodeIs( 403 );
40+
41+
/**
42+
* Since this is an invalid request, the JWT Auth and JWT Refresh token should not be returned in the response headers
43+
*/
44+
$I->dontSeeHttpHeader( 'X-JWT-Refresh' );
45+
$I->dontSeeHttpHeader( 'X-JWT-Auth' );
46+
47+
48+
/**
49+
* The response should be JSON
50+
*/
51+
$I->seeResponseIsJson();
52+
53+
/**
54+
* Get the JSON response
55+
*/
56+
$response = $I->grabResponse();
57+
58+
/**
59+
* Convert the response to JSON for making assertions
60+
*/
61+
$response_array = json_decode( $response, true );
62+
63+
/**
64+
* The request should have errors, because it attempted to query data for a user without providing Auth data
65+
*/
66+
$I->assertArrayHasKey( 'errors', $response_array );
67+
68+
/**
69+
* A valid request should contain the data in the response
70+
*/
71+
$I->assertArrayHasKey( 'data', $response_array );
72+
73+
/**
74+
* The viewer should be null in the response, because an unauthenticated request should not be able to access viewer data
75+
*/
76+
$I->assertNull( $response_array['data']['viewer'] );
77+
78+
/**
79+
* An unauthenticated request should still be able to access public data though, so let's make sure there's a post returned
80+
*/
81+
$I->assertNotEmpty( $response_array['data']['posts']['edges'][0]['node']['id'] );
82+
$I->assertNotEmpty( $response_array['data']['posts']['edges'][0]['node']['title'] );
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
$I = new FunctionalTester($scenario);
3+
$I->wantTo('Login with valid username and password and retrieve accessToken and refreshToken');
4+
5+
$username = uniqid();
6+
$user = $I->haveUserInDatabase( $username, 'administrator', [ 'user_pass' => 'password' ] );
7+
8+
/**
9+
* Login with username and password
10+
*/
11+
$I->sendPOST( 'http://wp.localhost/graphql', json_encode([
12+
'query' => '
13+
mutation Login($input: LoginInput!) {
14+
login( input: $input ) {
15+
authToken
16+
refreshToken
17+
user {
18+
username
19+
}
20+
}
21+
}',
22+
'variables' => [
23+
'input' => [
24+
'username' => $username,
25+
'password' => 'password',
26+
'clientMutationId' => uniqid(),
27+
]
28+
],
29+
], true));
30+
31+
$I->seeResponseCodeIs( 200 );
32+
$I->seeResponseIsJson();
33+
34+
$response = $I->grabResponse();
35+
$response_array = json_decode( $response, true );
36+
$I->assertArrayNotHasKey( 'errors', $response_array );
37+
$I->assertArrayHasKey( 'data', $response_array );
38+
$I->assertNotEmpty( $response_array['data']['login']['authToken'] );
39+
$I->assertNotEmpty( $response_array['data']['login']['refreshToken'] );
40+
$I->assertNotEmpty( $response_array['data']['login']['user']['username'] );
41+
$I->assertEquals( $username, $response_array['data']['login']['user']['username'] );

0 commit comments

Comments
 (0)