Skip to content

Commit 87a91a5

Browse files
authored
Merge branch 'master' into feat/client-ip-forwarding
2 parents c1495ca + c553b10 commit 87a91a5

7 files changed

Lines changed: 267 additions & 249 deletions

File tree

cmd/migrate_cmd.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cmd
22

33
import (
44
"embed"
5-
"fmt"
65
"net/url"
76
"os"
87

@@ -23,12 +22,12 @@ var migrateCmd = cobra.Command{
2322

2423
func migrate(cmd *cobra.Command, args []string) {
2524
globalConfig := loadGlobalConfig(cmd.Context())
25+
u, err := url.Parse(globalConfig.DB.URL)
26+
if err != nil {
27+
logrus.Fatalf("%+v", errors.Wrap(err, "parsing db connection url"))
28+
}
2629

2730
if globalConfig.DB.Driver == "" && globalConfig.DB.URL != "" {
28-
u, err := url.Parse(globalConfig.DB.URL)
29-
if err != nil {
30-
logrus.Fatalf("%+v", errors.Wrap(err, "parsing db connection url"))
31-
}
3231
globalConfig.DB.Driver = u.Scheme
3332
}
3433

@@ -53,16 +52,12 @@ func migrate(cmd *cobra.Command, args []string) {
5352
}
5453
}
5554

56-
u, _ := url.Parse(globalConfig.DB.URL)
57-
processedUrl := globalConfig.DB.URL
58-
if len(u.Query()) != 0 {
59-
processedUrl = fmt.Sprintf("%s&application_name=gotrue_migrations", processedUrl)
60-
} else {
61-
processedUrl = fmt.Sprintf("%s?application_name=gotrue_migrations", processedUrl)
62-
}
55+
q := u.Query()
56+
q.Add("application_name", "auth_migrations")
57+
u.RawQuery = q.Encode()
6358
deets := &pop.ConnectionDetails{
6459
Dialect: globalConfig.DB.Driver,
65-
URL: processedUrl,
60+
URL: u.String(),
6661
}
6762
deets.Options = map[string]string{
6863
"migration_table_name": "schema_migrations",

internal/api/e2e_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,90 @@ func TestE2EHooks(t *testing.T) {
971971
}
972972
})
973973
}
974+
975+
t.Run("AMRStringArrayUnmarshalling", func(t *testing.T) {
976+
defer inst.HookRecorder.CustomizeAccessToken.ClearCalls()
977+
978+
// Setup hook that returns amr as array of strings
979+
var claimsIn M
980+
hr := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
981+
w.Header().Add("content-type", "application/json")
982+
w.WriteHeader(http.StatusOK)
983+
984+
err := json.NewDecoder(r.Body).Decode(&claimsIn)
985+
require.NoError(t, err)
986+
987+
// Modify amr to be array of strings instead of objects
988+
claimsOut := copyMap(t, claimsIn)
989+
claimsOut["claims"].(M)["amr"] = []string{"password", "totp"}
990+
991+
err = json.NewEncoder(w).Encode(claimsOut)
992+
require.NoError(t, err)
993+
})
994+
995+
inst.HookRecorder.CustomizeAccessToken.ClearCalls()
996+
inst.HookRecorder.CustomizeAccessToken.SetHandler(hr)
997+
998+
// Get token with modified amr
999+
req := &api.PasswordGrantParams{
1000+
Email: string(currentUser.Email),
1001+
Password: defaultPassword,
1002+
}
1003+
1004+
res := new(api.AccessTokenResponse)
1005+
err := e2eapi.Do(ctx, http.MethodPost, inst.APIServer.URL+"/token?grant_type=password", req, res)
1006+
require.NoError(t, err)
1007+
require.True(t, len(res.Token) > 0)
1008+
1009+
// Verify hook was called
1010+
{
1011+
calls := inst.HookRecorder.CustomizeAccessToken.GetCalls()
1012+
require.Equal(t, 1, len(calls))
1013+
}
1014+
1015+
// Parse token to verify it can be unmarshalled
1016+
p := jwt.NewParser(jwt.WithValidMethods(globalCfg.JWT.ValidMethods))
1017+
token, err := p.ParseWithClaims(
1018+
res.Token,
1019+
&api.AccessTokenClaims{},
1020+
func(token *jwt.Token) (any, error) {
1021+
if kid, ok := token.Header["kid"]; ok {
1022+
if kidStr, ok := kid.(string); ok {
1023+
return conf.FindPublicKeyByKid(kidStr, &globalCfg.JWT)
1024+
}
1025+
}
1026+
if alg, ok := token.Header["alg"]; ok {
1027+
if alg == jwt.SigningMethodHS256.Name {
1028+
return []byte(globalCfg.JWT.Secret), nil
1029+
}
1030+
}
1031+
return nil, fmt.Errorf("missing kid")
1032+
})
1033+
require.NoError(t, err, "Token should parse successfully even with string array amr")
1034+
1035+
fmt.Println("token hereee", res.Token)
1036+
// Verify claims were unmarshalled correctly
1037+
claims, ok := token.Claims.(*api.AccessTokenClaims)
1038+
require.True(t, ok, "Claims should be AccessTokenClaims type")
1039+
require.NotNil(t, claims.AuthenticationMethodReference, "AMR should not be nil")
1040+
require.Len(t, claims.AuthenticationMethodReference, 2, "AMR should have 2 entries")
1041+
require.Equal(t, "password", claims.AuthenticationMethodReference[0].Method)
1042+
require.Equal(t, "totp", claims.AuthenticationMethodReference[1].Method)
1043+
1044+
// Call /user endpoint with the token to verify it works end-to-end
1045+
httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, "/user", nil)
1046+
require.NoError(t, err)
1047+
1048+
httpRes, err := inst.DoAuth(httpReq, res.Token)
1049+
require.NoError(t, err, "Should be able to call /user endpoint with token containing string array amr")
1050+
require.Equal(t, http.StatusOK, httpRes.StatusCode, "/user endpoint should return 200 OK")
1051+
1052+
// Verify we got user data back
1053+
var userData models.User
1054+
err = json.NewDecoder(httpRes.Body).Decode(&userData)
1055+
require.NoError(t, err, "Should be able to decode user response")
1056+
require.Equal(t, currentUser.ID, userData.ID, "Should get the correct user")
1057+
})
9741058
})
9751059

9761060
t.Run("SendEmail", func(t *testing.T) {

internal/api/token_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,43 @@ end; $$ language plpgsql;`,
741741
"user_metadata": nil,
742742
},
743743
shouldError: false,
744+
}, {
745+
desc: "Modify amr to be array of strings",
746+
uri: "pg-functions://postgres/auth/custom_access_token_amr_strings",
747+
hookFunctionSQL: `
748+
create or replace function custom_access_token_amr_strings(input jsonb)
749+
returns jsonb as $$
750+
declare
751+
result jsonb;
752+
begin
753+
input := jsonb_set(input, '{claims,amr}', '["password", "mfa"]'::jsonb);
754+
result := jsonb_build_object('claims', input->'claims');
755+
return result;
756+
end; $$ language plpgsql;`,
757+
expectedClaims: map[string]interface{}{
758+
"amr": []interface{}{"password", "mfa"},
759+
},
760+
shouldError: false,
761+
}, {
762+
desc: "Modify amr to be array of objects",
763+
uri: "pg-functions://postgres/auth/custom_access_token_amr_objects",
764+
hookFunctionSQL: `
765+
create or replace function custom_access_token_amr_objects(input jsonb)
766+
returns jsonb as $$
767+
declare
768+
result jsonb;
769+
begin
770+
input := jsonb_set(input, '{claims,amr}', '[{"method": "password"}, {"method": "mfa"}]'::jsonb);
771+
result := jsonb_build_object('claims', input->'claims');
772+
return result;
773+
end; $$ language plpgsql;`,
774+
expectedClaims: map[string]interface{}{
775+
"amr": []interface{}{
776+
map[string]interface{}{"method": "password"},
777+
map[string]interface{}{"method": "mfa"},
778+
},
779+
},
780+
shouldError: false,
744781
},
745782
}
746783
for _, c := range cases {

0 commit comments

Comments
 (0)