Skip to content

Commit

Permalink
Add option to logout to revoke all sessions at once (#1381)
Browse files Browse the repository at this point in the history
Add option to logout to revoke all sessions at once
  • Loading branch information
Yoann-Abbes committed Aug 20, 2019
1 parent cc7d6e3 commit 6ca80dc
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 11 deletions.
10 changes: 8 additions & 2 deletions doc/1/api/controllers/auth/logout/index.md
Expand Up @@ -19,7 +19,7 @@ If there were any, real-time subscriptions are cancelled.
### HTTP

```http
URL: http://kuzzle:7512/_logout
URL: http://kuzzle:7512/_logout[?global]
Method: POST
Headers: Authorization: "Bearer <authentication token>"
```
Expand All @@ -30,7 +30,8 @@ Headers: Authorization: "Bearer <authentication token>"
{
"controller": "auth",
"action": "logout",
"jwt": "<authentication token>"
"jwt": "<authentication token>",
"global": "<true|false>"
}
```

Expand All @@ -40,6 +41,11 @@ Headers: Authorization: "Bearer <authentication token>"

- `jwt`: valid authentication token (for the HTTP protocol, the token is to be passed to the `Authorization` header instead)

### Optional:

* `global`: if `true`, also revokes all other active sessions instead of just the current one (default: `false`)


---

## Response
Expand Down
11 changes: 11 additions & 0 deletions features/kuzzle.feature
Expand Up @@ -467,6 +467,17 @@ Feature: Kuzzle functional tests
Then I check the JWT Token
And The token is invalid

@security
Scenario: logout all sessions at once
Given I create a user "useradmin" with id "useradmin-id"
When I log in as useradmin:testpwd expiring in 1h
Then I check the JWT Token
And The token is valid
Then I logout all sessions at once
Then I can't write the document
Then I check the JWT Token
And The token is invalid

@security
Scenario: refresh token
Given I create a user "useradmin" with id "useradmin-id"
Expand Down
20 changes: 20 additions & 0 deletions features/step_definitions/authentication.js
Expand Up @@ -66,6 +66,26 @@ Then(/^I log ?out$/, function (callback) {
});
});

Then(/^I logout all sessions at once/, function (callback) {
if (!this.currentUser || !this.currentUser.token) {
callback(new Error('Cannot retrieve jwt token'));
return false;
}

this.api.logout(this.currentUser.token, true)
.then(body => {
delete this.currentUser;
if (body.error) {
return callback(new Error(body.error.message));
}
callback();
})
.catch(error => {
delete this.currentUser;
callback(error);
});
});

Then(/^I check the JWT Token$/, function (callback) {
if (!this.currentToken || !this.currentToken.jwt) {
return callback(new Error('Cannot retrieve the JWT token'));
Expand Down
5 changes: 4 additions & 1 deletion features/support/api/apiBase.js
Expand Up @@ -807,14 +807,17 @@ class ApiBase {
return this.send(msg);
}

logout (jwtToken) {
logout (jwtToken, global = false) {
const
msg = {
controller: 'auth',
action: 'logout',
jwt: jwtToken
};

if (global) {
msg.global = true;
}
return this.send(msg);
}

Expand Down
18 changes: 10 additions & 8 deletions lib/api/controllers/authController.js
Expand Up @@ -95,14 +95,16 @@ class AuthController extends BaseController {
logout(request) {
assertIsAuthenticated(this.anonymousId, request);

return this.kuzzle.repositories.token.expire(request.context.token)
.then(() => ({acknowledged: true}))
.catch(err => {
const error = apiErrorsManager.getError('token_force_expire');
error.details = err;

return Bluebird.reject(error);
});
return this.tryGetBoolean(request, 'args.global')
? this.kuzzle.repositories.token.deleteByUserId(request.context.user._id)
: this.kuzzle.repositories.token.expire(request.context.token)
.then(() => ({acknowledged: true}))
.catch(err => {
const error = errorsManager.getError('token_force_expire');
error.details = err;

return Bluebird.reject(error);
});
}

/**
Expand Down
8 changes: 8 additions & 0 deletions test/api/controllers/authController.test.js
Expand Up @@ -189,6 +189,14 @@ describe('Test the auth controller', () => {
});
});

it('should expire all tokens at once', () => {
request.input.args.global = true;
return authController.logout(request)
.then(() => {
should(kuzzle.repositories.token.deleteByUserId).calledWith('foo');
});
});

it('should emit an error if the token cannot be expired', () => {
const error = new Error('Mocked error');
kuzzle.repositories.token.expire.rejects(error);
Expand Down

0 comments on commit 6ca80dc

Please sign in to comment.