Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
Mình đã giới thiệu về Refresh Token grant type trong OAuth 2.0 trong bài viết về Các loại grant types trong OAuth 2.0. Trong bài viết này, mình sẽ hướng dẫn các bạn cách chúng ta lấy mới access token sử dụng refresh token với Keycloak là như thế nào các bạn nhé!
Đầu tiên, mình sẽ tạo mới trong Keycloak một client với Authorization Code grant type để làm ví dụ:
Thực hiện lấy access token cho client này, các bạn sẽ thấy kết quả như sau:
{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJZR29TSXBPblRKeTJmbjhzLWk4WlBKMC1xWkxybEEtNVI4ZjhXbTFjYjVVIn0.eyJleHAiOjE2MzU2NDMxMDYsImlhdCI6MTYzNTY0MjgwNiwiYXV0aF90aW1lIjoxNjM1NjQyNzk0LCJqdGkiOiJjZTY4ZDIxYS04YzdkLTQ1YWItOWMxNC1jYzdkMTM5NWNmOGMiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvaHVvbmdkYW5qYXZhIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjhhNTA5MmZhLWQ4MTUtNDYyMC05MmFiLTRhYzg3NTQyNjg1YyIsInR5cCI6IkJlYXJlciIsImF6cCI6Imh1b25nZGFuamF2YV9hdXRob3JpemF0aW9uX2NvZGUiLCJub25jZSI6IjllZW1meGhiazYiLCJzZXNzaW9uX3N0YXRlIjoiN2E4ZGMxODktMTVlZS00ZWZhLWEwZGEtNmY5ZjdlY2Y2MjczIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiZGVmYXVsdC1yb2xlcy1odW9uZ2RhbmphdmEiXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiS2hhbmggTmd1eWVuIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiaHVvbmdkYW5qYXZhIiwibG9jYWxlIjoiZW4iLCJnaXZlbl9uYW1lIjoiS2hhbmgiLCJmYW1pbHlfbmFtZSI6Ik5ndXllbiIsImVtYWlsIjoiaHVvbmdkYW5qYXZhLmNvbUBnbWFpbC5jb20ifQ.JgklBAraEs__i2CHI0z7KQZBy5JeIVfBPzP4cORxNEcI53zbGEmsCqHZoNt0qwcURGc9EV77jzOnqlq6j7wWPhtwhpzOHT3JupqGD86CyaUJ9rh-GrhnKDy7YMTp9337X7J2qzp5YIIK0tmroGjNR7dIpHn1ThatNLM-8w2KnydT7DhYjHx1gaX7HKXAuaiBhNbR7PYrS1-FsbxT--QjNNs-mgTXl7OVnqpCPcRCt8pBPu15kPbP00i7zJ7AaSmZfPNBt7rFPGkaYAX5kIi8FY0wf2RZuFMcgDpg-01rzfTkoWIXCdDSQPBagAveQUE6viaC-nsV7_-5zTCvGch_0A", "expires_in": 300, "refresh_expires_in": 1800, "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI5ZjYzNWQ5Mi1hOTdjLTQwMjktODE2YS05ZWU3YjAxMmE3NDUifQ.eyJleHAiOjE2MzU2NDQ2MDYsImlhdCI6MTYzNTY0MjgwNiwianRpIjoiYTZmYmQwMDUtNGZlOC00NTdjLWEwZmEtMDA5ODBiZjY4NmE0IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2h1b25nZGFuamF2YSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9odW9uZ2RhbmphdmEiLCJzdWIiOiI4YTUwOTJmYS1kODE1LTQ2MjAtOTJhYi00YWM4NzU0MjY4NWMiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiaHVvbmdkYW5qYXZhX2F1dGhvcml6YXRpb25fY29kZSIsIm5vbmNlIjoiOWVlbWZ4aGJrNiIsInNlc3Npb25fc3RhdGUiOiI3YThkYzE4OS0xNWVlLTRlZmEtYTBkYS02ZjlmN2VjZjYyNzMiLCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIn0.zMYO5ZRupTskEs9QxJdnP3d24qcHavrLKG4pkQ-OLN8", "token_type": "Bearer", "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJZR29TSXBPblRKeTJmbjhzLWk4WlBKMC1xWkxybEEtNVI4ZjhXbTFjYjVVIn0.eyJleHAiOjE2MzU2NDMxMDYsImlhdCI6MTYzNTY0MjgwNiwiYXV0aF90aW1lIjoxNjM1NjQyNzk0LCJqdGkiOiI3NGFkNGMyNC1mYTliLTQ4MWMtYTgwZS1jMTVhNDk1NmU0ZDciLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvaHVvbmdkYW5qYXZhIiwiYXVkIjoiaHVvbmdkYW5qYXZhX2F1dGhvcml6YXRpb25fY29kZSIsInN1YiI6IjhhNTA5MmZhLWQ4MTUtNDYyMC05MmFiLTRhYzg3NTQyNjg1YyIsInR5cCI6IklEIiwiYXpwIjoiaHVvbmdkYW5qYXZhX2F1dGhvcml6YXRpb25fY29kZSIsIm5vbmNlIjoiOWVlbWZ4aGJrNiIsInNlc3Npb25fc3RhdGUiOiI3YThkYzE4OS0xNWVlLTRlZmEtYTBkYS02ZjlmN2VjZjYyNzMiLCJhdF9oYXNoIjoiRWpfZGlIMURTa0lINzlsMGpqMENsQSIsImFjciI6IjEiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJLaGFuaCBOZ3V5ZW4iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJodW9uZ2RhbmphdmEiLCJsb2NhbGUiOiJlbiIsImdpdmVuX25hbWUiOiJLaGFuaCIsImZhbWlseV9uYW1lIjoiTmd1eWVuIiwiZW1haWwiOiJodW9uZ2RhbmphdmEuY29tQGdtYWlsLmNvbSJ9.Cm9kcio4DYM8f3WN_KQ5UJIe-RX8rRLKP0wlLJqwObE0tIxXAEqVOzr7gAv9WEpy_wDO3mC7NN4ZyfX8iQlm47UCdnFo-50vbeUDdfa1wtSM2FfnPOhV_76KwVDgD1KqImlHGdb0QbvzxAMS25RPrWeYWQsoJlVFqcOC52_sqQ217vJyuytComZ7BJ_otiKLSiUTmAAgmhc3UAiXjKseefzv858fn8b0Z-tVEJh-CfjwhnazRsxj5ukGYknX56wJHNGT35JU_Wph7rzv9EsRNm36_0Vt-7LyLBKrwWWV55MoPL_il7nzxUrehhfSzAzBzOSMhYqCuSfAAErKUKJGHw", "not-before-policy": 0, "session_state": "7a8dc189-15ee-4efa-a0da-6f9f7ecf6273", "scope": "openid email profile" }
Nội dung của refresh token nếu mình decode nó sử dụng https://jwt.io/ cơ bản như sau:
Các bạn để ý đến claim “typ” trong nội dung của refresh token trên nhé! Ở đây giá trị của claim này là Refresh, chúng ta còn có một số type khác của refresh token nữa, chẳng hạn như Offline,…
Refresh token sẽ luôn được trả về cùng với access token, giúp chúng ta có thể lấy access token mới mà không cần user phải đăng nhập trở lại.
Để lấy access token mới với refresh token thì trong request để lấy access token, các bạn chỉ cần truyền grant_type=refresh_token, giá trị của refresh token mà chúng ta có trong request lấy access token trước, client ID và client secret.
Với ví dụ này của mình, các bạn có thể lấy access token mới bằng cách request như sau:
Một refresh token sẽ luôn có expiration time, mặc định của Keycloak là 30 phút các bạn nhé! Mỗi khi một access token mới được issue thì refresh token sẽ được issue lại, và các bạn có thể sử dụng cái mới nhất để expiration time được lâu hơn.
Chúng ta có thể sử dụng một refresh token để request access token nhiều lần. Tuy nhiên, các bạn có thể giới hạn số lần mà một refresh token có thể được sử dụng, bằng cách vào tab Tokens của Realm Settings, cấu hình field Revoke Refresh Token. Nếu các bạn turn on field này, thì có thể cấu hình số lần một refresh token được sử dụng lại:
Mặc định các bạn chỉ có thể sử dụng refresh token một lần thôi các bạn nhé!
Bài viết gốc được đăng tải tại huongdanjava.com
Có thể bạn quan tâm:
- JMeter: Sử dụng Regular Expression với Sessions ID và Tokens – P3
- Cấu hình expiration time cho access token với Spring Authorization Server
- Token revocation với Spring Authorization Server