From 62eb18d0fadc3d563223b50a33faeeef1a4adf56 Mon Sep 17 00:00:00 2001 From: akellbl4 Date: Sun, 12 Feb 2023 09:30:12 -0500 Subject: [PATCH] add ability to specify response_mode --- provider/apple.go | 13 ++++++++++++- provider/apple_test.go | 44 +++++++++++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/provider/apple.go b/provider/apple.go index 3ff3c9d..8b8258e 100644 --- a/provider/apple.go +++ b/provider/apple.go @@ -74,6 +74,7 @@ type AppleConfig struct { TeamID string // developer Team ID (10 characters), required for create JWT. It available, after signed in at developer account, by link: https://developer.apple.com/account/#/membership KeyID string // private key ID assigned to private key obtain in Apple developer account + responseMode string // changes method of receiving data in callback. Default value "form_post" (https://developer.apple.com/documentation/sign_in_with_apple/request_an_authorization_to_the_sign_in_with_apple_server?changes=_1_2#4066168) scopes []string // for this package allow only username scope and UID in token claims. Apple service API provide only "email" and "name" scope values (https://developer.apple.com/documentation/sign_in_with_apple/clientconfigi/3230955-scope) privateKey interface{} // private key from Apple obtained in developer account (the keys section). Required for create the Client Secret (https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens#3262048) publicKey crypto.PublicKey // need for validate sign of token @@ -156,6 +157,11 @@ func NewApple(p Params, appleCfg AppleConfig, privateKeyLoader PrivateKeyLoaderI return nil, fmt.Errorf("required params missed: %s", strings.Join(emptyParams, ", ")) } + responseMode := "form_post" + if appleCfg.responseMode != "" { + responseMode = appleCfg.responseMode + } + ah := AppleHandler{ Params: p, name: "apple", // static name for an Apple provider @@ -166,6 +172,7 @@ func NewApple(p Params, appleCfg AppleConfig, privateKeyLoader PrivateKeyLoaderI KeyID: appleCfg.KeyID, scopes: []string{"name"}, jwkURL: appleKeysURL, + responseMode: responseMode, }, endpoint: oauth2.Endpoint{ @@ -503,6 +510,10 @@ func (ah *AppleHandler) prepareLoginURL(state, path string) (string, error) { scopesList := strings.Join(ah.conf.scopes, " ") + if scopesList != "" && ah.conf.responseMode != "form_post" { + return "", fmt.Errorf("response_mode must be form_post if scope is not empty") + } + authURL, err := url.Parse(ah.endpoint.AuthURL) if err != nil { return "", err @@ -511,7 +522,7 @@ func (ah *AppleHandler) prepareLoginURL(state, path string) (string, error) { query := authURL.Query() query.Set("state", state) query.Set("response_type", "code") - query.Set("response_mode", "form_post") + query.Set("response_mode", ah.conf.responseMode) query.Set("client_id", ah.conf.ClientID) query.Set("scope", scopesList) query.Set("redirect_uri", ah.makeRedirURL(path)) diff --git a/provider/apple_test.go b/provider/apple_test.go index ccaf493..59727ea 100644 --- a/provider/apple_test.go +++ b/provider/apple_test.go @@ -159,7 +159,7 @@ func TestAppleHandlerCreateClientSecret(t *testing.T) { assert.Error(t, err) assert.Empty(t, tkn) - ah, err = prepareAppleHandlerTest() + ah, err = prepareAppleHandlerTest("", []string{}) assert.NoError(t, err) assert.IsType(t, &AppleHandler{}, ah) @@ -197,7 +197,7 @@ func TestAppleParseUserData(t *testing.T) { } func TestPrepareLoginURL(t *testing.T) { - ah, err := prepareAppleHandlerTest() + ah, err := prepareAppleHandlerTest("", []string{}) assert.NoError(t, err) assert.IsType(t, &AppleHandler{}, ah) @@ -214,6 +214,34 @@ func TestPrepareLoginURL(t *testing.T) { assert.Equal(t, q.Get("client_id"), ah.conf.ClientID) } +func TestPrepareLoginURLWithCustomResponseMode(t *testing.T) { + ah, err := prepareAppleHandlerTest("query", []string{}) + assert.NoError(t, err) + assert.IsType(t, &AppleHandler{}, ah) + + lURL, err := ah.prepareLoginURL("1112233", "apple-test/login") + assert.NoError(t, err) + assert.True(t, strings.HasPrefix(lURL, ah.endpoint.AuthURL)) + + checkURL, err := url.Parse(lURL) + assert.NoError(t, err) + q := checkURL.Query() + assert.Equal(t, q.Get("state"), "1112233") + assert.Equal(t, q.Get("response_type"), "code") + assert.Equal(t, q.Get("response_mode"), "query") + assert.Equal(t, q.Get("client_id"), ah.conf.ClientID) +} + +func TestThrowsWhenNotEmptyScopeAndWrongResponseMode(t *testing.T) { + ah, err := prepareAppleHandlerTest("query", []string{"email"}) + assert.NoError(t, err) + assert.IsType(t, &AppleHandler{}, ah) + + lURL, err := ah.prepareLoginURL("1112233", "apple-test/login") + assert.Equal(t, "", lURL) + assert.Error(t, err) +} + func TestAppleHandlerMakeRedirURL(t *testing.T) { cases := []struct{ rootURL, route, out string }{ {"localhost:8080/", "/my/auth/path/apple", "localhost:8080/my/auth/path/callback"}, @@ -224,7 +252,7 @@ func TestAppleHandlerMakeRedirURL(t *testing.T) { {"mysite.com", "", "mysite.com/callback"}, } - ah, err := prepareAppleHandlerTest() + ah, err := prepareAppleHandlerTest("", []string{}) assert.NoError(t, err) assert.IsType(t, &AppleHandler{}, ah) @@ -316,7 +344,7 @@ func TestAppleHandler_Exchange(t *testing.T) { teardown := prepareAppleOauthTest(t, 8981, 8982, &testResponseToken) defer teardown() - ah, err := prepareAppleHandlerTest() + ah, err := prepareAppleHandlerTest("", []string{}) require.Nil(t, err) ah.endpoint = oauth2.Endpoint{ @@ -389,8 +417,7 @@ Ivx5tHkv return filePath, cancelCtx } -func prepareAppleHandlerTest() (*AppleHandler, error) { - +func prepareAppleHandlerTest(responseMode string, scopes []string) (*AppleHandler, error) { p := Params{ URL: "http://localhost", Issuer: "test-issuer", @@ -402,14 +429,17 @@ func prepareAppleHandlerTest() (*AppleHandler, error) { ClientID: "auth.example.com", TeamID: "AA11BB22CC", KeyID: "BS2A79VCTT", + responseMode: responseMode, + scopes: scopes, } + cl := customLoader{} return NewApple(p, aCfg, cl) } func prepareAppleOauthTest(t *testing.T, loginPort, authPort int, testToken *string) func() { signKey, testJWK := createTestSignKeyPairs(t) - provider, err := prepareAppleHandlerTest() + provider, err := prepareAppleHandlerTest("", []string{}) assert.NoError(t, err) assert.IsType(t, &AppleHandler{}, provider)