diff --git a/assets/src/bundles/auth/index.js b/assets/src/bundles/auth/index.js --- a/assets/src/bundles/auth/index.js +++ b/assets/src/bundles/auth/index.js @@ -49,8 +49,14 @@
${token}
`; swh.webapp.showModalHtml('Display bearer token', tokenHtml); }) - .catch(() => { - swh.webapp.showModalHtml('Display bearer token', errorMessage('Internal server error.')); + .catch(response => { + response.text().then(responseText => { + let errorMsg = 'Internal server error.'; + if (response.status === 400) { + errorMsg = responseText; + } + swh.webapp.showModalHtml('Display bearer token', errorMessage(errorMsg)); + }); }); } diff --git a/cypress/integration/api-tokens.spec.js b/cypress/integration/api-tokens.spec.js --- a/cypress/integration/api-tokens.spec.js +++ b/cypress/integration/api-tokens.spec.js @@ -59,9 +59,9 @@ .should('contain', 'You are not allowed to generate bearer tokens'); }); - function displayToken(Urls, status, tokenValue = '') { + function displayToken(Urls, status, body = '') { cy.intercept('POST', `${Urls.oidc_get_bearer_token()}/**`, { - body: tokenValue, + body: body, statusCode: status }).as('getTokenRequest'); @@ -83,11 +83,20 @@ .should('contain', tokenValue); }); - it('should report errors when token display failed', function() { + it('should report error when token display failed', function() { initTokensPage(this.Urls, [{id: 1, creation_date: new Date().toISOString()}]); - displayToken(this.Urls, 500); + const errorMessage = 'Internal server error'; + displayToken(this.Urls, 500, errorMessage); cy.get('.modal-body') - .should('contain', 'Internal server error'); + .should('contain', errorMessage); + }); + + it('should report error when token expired', function() { + initTokensPage(this.Urls, [{id: 1, creation_date: new Date().toISOString()}]); + const errorMessage = 'Bearer token has expired'; + displayToken(this.Urls, 400, errorMessage); + cy.get('.modal-body') + .should('contain', errorMessage); }); function revokeToken(Urls, status) { diff --git a/swh/web/auth/views.py b/swh/web/auth/views.py --- a/swh/web/auth/views.py +++ b/swh/web/auth/views.py @@ -14,6 +14,7 @@ from django.http import HttpRequest from django.http.response import ( HttpResponse, + HttpResponseBadRequest, HttpResponseForbidden, HttpResponseRedirect, JsonResponse, @@ -25,6 +26,7 @@ from swh.auth.django.utils import keycloak_oidc_client from swh.auth.django.views import get_oidc_login_data, oidc_login_view from swh.auth.django.views import urlpatterns as auth_urlpatterns +from swh.auth.keycloak import KeycloakError, keycloak_error_message from swh.web.auth.models import OIDCUserOfflineTokens from swh.web.auth.utils import decrypt_data, encrypt_data from swh.web.common.exc import ForbiddenExc @@ -111,9 +113,21 @@ decrypted_token = decrypt_data( _encrypted_token_bytes(token_data.offline_token), secret, salt ) - return HttpResponse(decrypted_token.decode("ascii"), content_type="text/plain") + refresh_token = decrypted_token.decode("ascii") + # check token is still valid + oidc_client = keycloak_oidc_client() + oidc_client.refresh_token(refresh_token) + return HttpResponse(refresh_token, content_type="text/plain") except InvalidToken: return HttpResponse(status=401) + except KeycloakError as ke: + error_msg = keycloak_error_message(ke) + if error_msg in ( + "invalid_grant: Offline session not active", + "invalid_grant: Offline user session not found", + ): + error_msg = "Bearer token has expired, please generate a new one." + return HttpResponseBadRequest(error_msg, content_type="text/plain") @require_http_methods(["POST"]) diff --git a/swh/web/templates/auth/profile.html b/swh/web/templates/auth/profile.html --- a/swh/web/templates/auth/profile.html +++ b/swh/web/templates/auth/profile.html @@ -77,6 +77,9 @@ For instance when using curl proceed as follows:
curl -H "Authorization: Bearer ${TOKEN}" {{ site_base_url }}api/...

+

+ Please not that a bearer token will automatically expire after 30 days of inactivity. +