Skip to content

Commit

Permalink
add ability to specify response_mode
Browse files Browse the repository at this point in the history
  • Loading branch information
akellbl4 authored and umputun committed Apr 1, 2023
1 parent 8a02ea3 commit 62eb18d
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 8 deletions.
13 changes: 12 additions & 1 deletion provider/apple.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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{
Expand Down Expand Up @@ -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
Expand All @@ -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))
Expand Down
44 changes: 37 additions & 7 deletions provider/apple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand All @@ -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"},
Expand All @@ -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)

Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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",
Expand All @@ -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)

Expand Down

0 comments on commit 62eb18d

Please sign in to comment.