mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-04-22 15:57:52 +02:00
[#1623] added apis.RecordAuthResponse helper
This commit is contained in:
parent
a15b192a42
commit
e25c252fc2
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
- Refactored all `forms` Submit interceptors to use a Generic data type as their payload.
|
- Refactored all `forms` Submit interceptors to use a Generic data type as their payload.
|
||||||
|
|
||||||
|
- Added new helper `apis.RecordAuthResponse(app, httpContext, record, meta)` to return a standard Record auth API response ([#1623](https://github.com/pocketbase/pocketbase/issues/1623)).
|
||||||
|
|
||||||
|
|
||||||
## v0.11.2
|
## v0.11.2
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
@ -14,7 +13,6 @@ import (
|
|||||||
"github.com/pocketbase/pocketbase/forms"
|
"github.com/pocketbase/pocketbase/forms"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"github.com/pocketbase/pocketbase/resolvers"
|
"github.com/pocketbase/pocketbase/resolvers"
|
||||||
"github.com/pocketbase/pocketbase/tokens"
|
|
||||||
"github.com/pocketbase/pocketbase/tools/auth"
|
"github.com/pocketbase/pocketbase/tools/auth"
|
||||||
"github.com/pocketbase/pocketbase/tools/routine"
|
"github.com/pocketbase/pocketbase/tools/routine"
|
||||||
"github.com/pocketbase/pocketbase/tools/search"
|
"github.com/pocketbase/pocketbase/tools/search"
|
||||||
@ -51,53 +49,6 @@ type recordAuthApi struct {
|
|||||||
app core.App
|
app core.App
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *recordAuthApi) authResponse(c echo.Context, authRecord *models.Record, meta any) error {
|
|
||||||
token, tokenErr := tokens.NewRecordAuthToken(api.app, authRecord)
|
|
||||||
if tokenErr != nil {
|
|
||||||
return NewBadRequestError("Failed to create auth token.", tokenErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
event := &core.RecordAuthEvent{
|
|
||||||
HttpContext: c,
|
|
||||||
Record: authRecord,
|
|
||||||
Token: token,
|
|
||||||
Meta: meta,
|
|
||||||
}
|
|
||||||
|
|
||||||
return api.app.OnRecordAuthRequest().Trigger(event, func(e *core.RecordAuthEvent) error {
|
|
||||||
// allow always returning the email address of the authenticated account
|
|
||||||
e.Record.IgnoreEmailVisibility(true)
|
|
||||||
|
|
||||||
// expand record relations
|
|
||||||
expands := strings.Split(c.QueryParam(expandQueryParam), ",")
|
|
||||||
if len(expands) > 0 {
|
|
||||||
// create a copy of the cached request data and adjust it to the current auth record
|
|
||||||
requestData := *RequestData(e.HttpContext)
|
|
||||||
requestData.Admin = nil
|
|
||||||
requestData.AuthRecord = e.Record
|
|
||||||
failed := api.app.Dao().ExpandRecord(
|
|
||||||
e.Record,
|
|
||||||
expands,
|
|
||||||
expandFetch(api.app.Dao(), &requestData),
|
|
||||||
)
|
|
||||||
if len(failed) > 0 && api.app.IsDebug() {
|
|
||||||
log.Println("Failed to expand relations: ", failed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result := map[string]any{
|
|
||||||
"token": e.Token,
|
|
||||||
"record": e.Record,
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Meta != nil {
|
|
||||||
result["meta"] = e.Meta
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.HttpContext.JSON(http.StatusOK, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *recordAuthApi) authRefresh(c echo.Context) error {
|
func (api *recordAuthApi) authRefresh(c echo.Context) error {
|
||||||
record, _ := c.Get(ContextAuthRecordKey).(*models.Record)
|
record, _ := c.Get(ContextAuthRecordKey).(*models.Record)
|
||||||
if record == nil {
|
if record == nil {
|
||||||
@ -110,7 +61,7 @@ func (api *recordAuthApi) authRefresh(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlerErr := api.app.OnRecordBeforeAuthRefreshRequest().Trigger(event, func(e *core.RecordAuthRefreshEvent) error {
|
handlerErr := api.app.OnRecordBeforeAuthRefreshRequest().Trigger(event, func(e *core.RecordAuthRefreshEvent) error {
|
||||||
return api.authResponse(e.HttpContext, e.Record, nil)
|
return RecordAuthResponse(api.app, e.HttpContext, e.Record, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
if handlerErr == nil {
|
if handlerErr == nil {
|
||||||
@ -273,7 +224,7 @@ func (api *recordAuthApi) authWithOAuth2(c echo.Context) error {
|
|||||||
e.Record = data.Record
|
e.Record = data.Record
|
||||||
e.OAuth2User = data.OAuth2User
|
e.OAuth2User = data.OAuth2User
|
||||||
|
|
||||||
return api.authResponse(e.HttpContext, e.Record, e.OAuth2User)
|
return RecordAuthResponse(api.app, e.HttpContext, e.Record, e.OAuth2User)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -313,7 +264,7 @@ func (api *recordAuthApi) authWithPassword(c echo.Context) error {
|
|||||||
return NewBadRequestError("Failed to authenticate.", err)
|
return NewBadRequestError("Failed to authenticate.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.authResponse(e.HttpContext, e.Record, nil)
|
return RecordAuthResponse(api.app, e.HttpContext, e.Record, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -2,13 +2,17 @@ package apis
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
"github.com/pocketbase/dbx"
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
"github.com/pocketbase/pocketbase/daos"
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"github.com/pocketbase/pocketbase/resolvers"
|
"github.com/pocketbase/pocketbase/resolvers"
|
||||||
|
"github.com/pocketbase/pocketbase/tokens"
|
||||||
"github.com/pocketbase/pocketbase/tools/rest"
|
"github.com/pocketbase/pocketbase/tools/rest"
|
||||||
"github.com/pocketbase/pocketbase/tools/search"
|
"github.com/pocketbase/pocketbase/tools/search"
|
||||||
)
|
)
|
||||||
@ -41,6 +45,53 @@ func RequestData(c echo.Context) *models.RequestData {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RecordAuthResponse(app core.App, c echo.Context, authRecord *models.Record, meta any) error {
|
||||||
|
token, tokenErr := tokens.NewRecordAuthToken(app, authRecord)
|
||||||
|
if tokenErr != nil {
|
||||||
|
return NewBadRequestError("Failed to create auth token.", tokenErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
event := &core.RecordAuthEvent{
|
||||||
|
HttpContext: c,
|
||||||
|
Record: authRecord,
|
||||||
|
Token: token,
|
||||||
|
Meta: meta,
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.OnRecordAuthRequest().Trigger(event, func(e *core.RecordAuthEvent) error {
|
||||||
|
// allow always returning the email address of the authenticated account
|
||||||
|
e.Record.IgnoreEmailVisibility(true)
|
||||||
|
|
||||||
|
// expand record relations
|
||||||
|
expands := strings.Split(c.QueryParam(expandQueryParam), ",")
|
||||||
|
if len(expands) > 0 {
|
||||||
|
// create a copy of the cached request data and adjust it to the current auth record
|
||||||
|
requestData := *RequestData(e.HttpContext)
|
||||||
|
requestData.Admin = nil
|
||||||
|
requestData.AuthRecord = e.Record
|
||||||
|
failed := app.Dao().ExpandRecord(
|
||||||
|
e.Record,
|
||||||
|
expands,
|
||||||
|
expandFetch(app.Dao(), &requestData),
|
||||||
|
)
|
||||||
|
if len(failed) > 0 && app.IsDebug() {
|
||||||
|
log.Println("Failed to expand relations: ", failed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[string]any{
|
||||||
|
"token": e.Token,
|
||||||
|
"record": e.Record,
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Meta != nil {
|
||||||
|
result["meta"] = e.Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.HttpContext.JSON(http.StatusOK, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// EnrichRecord parses the request context and enrich the provided record:
|
// EnrichRecord parses the request context and enrich the provided record:
|
||||||
// - expands relations (if defaultExpands and/or ?expand query param is set)
|
// - expands relations (if defaultExpands and/or ?expand query param is set)
|
||||||
// - ensures that the emails of the auth record and its expanded auth relations
|
// - ensures that the emails of the auth record and its expanded auth relations
|
||||||
|
@ -59,6 +59,118 @@ func TestRequestData(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRecordAuthResponse(t *testing.T) {
|
||||||
|
app, _ := tests.NewTestApp()
|
||||||
|
defer app.Cleanup()
|
||||||
|
|
||||||
|
dummyAdmin := &models.Admin{}
|
||||||
|
dummyAdmin.Id = "id1"
|
||||||
|
|
||||||
|
nonAuthRecord, err := app.Dao().FindRecordById("demo1", "al1h9ijdeojtsjy")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authRecord, err := app.Dao().FindRecordById("users", "4q1xlclmfloku33")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
record *models.Record
|
||||||
|
meta any
|
||||||
|
expectError bool
|
||||||
|
expectedContent []string
|
||||||
|
notExpectedContent []string
|
||||||
|
expectedEvents map[string]int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "non auth record",
|
||||||
|
record: nonAuthRecord,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid auth record - without meta",
|
||||||
|
record: authRecord,
|
||||||
|
expectError: false,
|
||||||
|
expectedContent: []string{
|
||||||
|
`"token":"`,
|
||||||
|
`"record":{`,
|
||||||
|
`"id":"`,
|
||||||
|
`"expand":{"rel":{`,
|
||||||
|
},
|
||||||
|
notExpectedContent: []string{
|
||||||
|
`"meta":`,
|
||||||
|
},
|
||||||
|
expectedEvents: map[string]int{
|
||||||
|
"OnRecordAuthRequest": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid auth record - with meta",
|
||||||
|
record: authRecord,
|
||||||
|
meta: map[string]any{"meta_test": 123},
|
||||||
|
expectError: false,
|
||||||
|
expectedContent: []string{
|
||||||
|
`"token":"`,
|
||||||
|
`"record":{`,
|
||||||
|
`"id":"`,
|
||||||
|
`"expand":{"rel":{`,
|
||||||
|
`"meta":{"meta_test":123`,
|
||||||
|
},
|
||||||
|
expectedEvents: map[string]int{
|
||||||
|
"OnRecordAuthRequest": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
app.ResetEventCalls()
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/?expand=rel", nil)
|
||||||
|
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c := e.NewContext(req, rec)
|
||||||
|
c.Set(apis.ContextAdminKey, dummyAdmin)
|
||||||
|
|
||||||
|
responseErr := apis.RecordAuthResponse(app, c, s.record, s.meta)
|
||||||
|
|
||||||
|
hasErr := responseErr != nil
|
||||||
|
if hasErr != s.expectError {
|
||||||
|
t.Fatalf("[%s] Expected hasErr to be %v, got %v (%v)", s.name, s.expectError, hasErr, responseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(app.EventCalls) != len(s.expectedEvents) {
|
||||||
|
t.Fatalf("[%s] Expected events \n%v, \ngot \n%v", s.name, s.expectedEvents, app.EventCalls)
|
||||||
|
}
|
||||||
|
for k, v := range s.expectedEvents {
|
||||||
|
if app.EventCalls[k] != v {
|
||||||
|
t.Fatalf("[%s] Expected event %s to be called %d times, got %d", s.name, k, v, app.EventCalls[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasErr {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response := rec.Body.String()
|
||||||
|
|
||||||
|
for _, v := range s.expectedContent {
|
||||||
|
if !strings.Contains(response, v) {
|
||||||
|
t.Fatalf("[%s] Missing %v in response \n%v", s.name, v, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range s.notExpectedContent {
|
||||||
|
if strings.Contains(response, v) {
|
||||||
|
t.Fatalf("[%s] Unexpected %v in response \n%v", s.name, v, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEnrichRecords(t *testing.T) {
|
func TestEnrichRecords(t *testing.T) {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
req := httptest.NewRequest(http.MethodGet, "/?expand=rel_many", nil)
|
req := httptest.NewRequest(http.MethodGet, "/?expand=rel_many", nil)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user