1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-11-28 16:50:11 +02:00

[#718] enabled calling auth-refresh with impersonate token

This commit is contained in:
Gani Georgiev
2025-06-29 11:24:50 +03:00
parent f318f461ea
commit a9c42d0282
3 changed files with 35 additions and 11 deletions

View File

@@ -1,5 +1,9 @@
## v0.29.0 (WIP) ## v0.29.0 (WIP)
- Enabled calling the `/auth-refresh` endpoint with nonrenewable tokens.
_When used with nonrenewable tokens (e.g. impersonate) the endpoint will simply return the same token with the up-to-date user data associated with it._
- Added the triggered rate rimit rule in the error log `details`. - Added the triggered rate rimit rule in the error log `details`.
- Other minor improvements (wrapped the backup restore in a transaction as an extra precaution, updated npm deps, regenerated JSVM docs with the recent tygoja changes, etc.). - Other minor improvements (wrapped the backup restore in a transaction as an extra precaution, updated npm deps, regenerated JSVM docs with the recent tygoja changes, etc.).
@@ -502,7 +506,7 @@ There are a lot of changes but to highlight some of the most notable ones:
- Admins are now system `_superusers` auth records. - Admins are now system `_superusers` auth records.
- Builtin rate limiter (_supports tags, wildcards and exact routes matching_). - Builtin rate limiter (_supports tags, wildcards and exact routes matching_).
- Batch/transactional Web API endpoint. - Batch/transactional Web API endpoint.
- Impersonate Web API endpoint (_it could be also used for generating fixed/non-refreshable superuser tokens, aka. "API keys"_). - Enabled Web API endpoint (_it could be also used for generating fixed/nonrenewable superuser tokens, aka. "API keys"_).
- Support for custom user request activity log attributes. - Support for custom user request activity log attributes.
- One-Time Password (OTP) auth method (_via email code_). - One-Time Password (OTP) auth method (_via email code_).
- Multi-Factor Authentication (MFA) support (_currently requires any 2 different auth methods to be used_). - Multi-Factor Authentication (MFA) support (_currently requires any 2 different auth methods to be used_).

View File

@@ -12,18 +12,24 @@ func recordAuthRefresh(e *core.RequestEvent) error {
return e.NotFoundError("Missing auth record context.", nil) return e.NotFoundError("Missing auth record context.", nil)
} }
currentToken := getAuthTokenFromRequest(e)
claims, _ := security.ParseUnverifiedJWT(currentToken)
if v, ok := claims[core.TokenClaimRefreshable]; !ok || !cast.ToBool(v) {
return e.ForbiddenError("The current auth token is not refreshable.", nil)
}
event := new(core.RecordAuthRefreshRequestEvent) event := new(core.RecordAuthRefreshRequestEvent)
event.RequestEvent = e event.RequestEvent = e
event.Collection = record.Collection() event.Collection = record.Collection()
event.Record = record event.Record = record
return e.App.OnRecordAuthRefreshRequest().Trigger(event, func(e *core.RecordAuthRefreshRequestEvent) error { return e.App.OnRecordAuthRefreshRequest().Trigger(event, func(e *core.RecordAuthRefreshRequestEvent) error {
return RecordAuthResponse(e.RequestEvent, e.Record, "", nil) token := getAuthTokenFromRequest(e.RequestEvent)
// skip token renewal if the token's payload doesn't explicitly allow it (e.g. impersonate tokens)
claims, _ := security.ParseUnverifiedJWT(token) //
if v, ok := claims[core.TokenClaimRefreshable]; ok && cast.ToBool(v) {
var tokenErr error
token, tokenErr = e.Record.NewAuthToken()
if tokenErr != nil {
return e.InternalServerError("Failed to refresh auth token.", tokenErr)
}
}
return recordAuthResponse(e.RequestEvent, e.Record, token, "", nil)
}) })
} }

View File

@@ -73,6 +73,8 @@ func TestRecordAuthRefresh(t *testing.T) {
}, },
NotExpectedContent: []string{ NotExpectedContent: []string{
`"missing":`, `"missing":`,
// should return a different token
"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.ZT3F0Z3iM-xbGgSG3LEKiEzHrPHr8t8IuHLZGGNuxLo",
}, },
ExpectedEvents: map[string]int{ ExpectedEvents: map[string]int{
"*": 0, "*": 0,
@@ -88,9 +90,21 @@ func TestRecordAuthRefresh(t *testing.T) {
Headers: map[string]string{ Headers: map[string]string{
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6ZmFsc2V9.4IsO6YMsR19crhwl_YWzvRH8pfq2Ri4Gv2dzGyneLak", "Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6ZmFsc2V9.4IsO6YMsR19crhwl_YWzvRH8pfq2Ri4Gv2dzGyneLak",
}, },
ExpectedStatus: 403, ExpectedStatus: 200,
ExpectedContent: []string{`"data":{}`}, ExpectedContent: []string{
ExpectedEvents: map[string]int{"*": 0}, // should return the same token
`"token":"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6ZmFsc2V9.4IsO6YMsR19crhwl_YWzvRH8pfq2Ri4Gv2dzGyneLak"`,
`"record":`,
`"id":"4q1xlclmfloku33"`,
`"emailVisibility":false`,
`"email":"test@example.com"`, // the owner can always view their email address
},
ExpectedEvents: map[string]int{
"*": 0,
"OnRecordAuthRefreshRequest": 1,
"OnRecordAuthRequest": 1,
"OnRecordEnrich": 1,
},
}, },
{ {
Name: "unverified auth record in onlyVerified collection", Name: "unverified auth record in onlyVerified collection",