Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions providers/vk/authorize_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package vk

import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
)

// tokenServer spins up an httptest.Server that responds to the OAuth2 token
// exchange with the given JSON body, and swaps the package-level tokenURL so
// the provider hits it. The returned cleanup restores the original tokenURL
// and shuts the server down.
func tokenServer(t *testing.T, body string) func() {
t.Helper()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, body)
}))
original := tokenURL
tokenURL = server.URL
return func() {
tokenURL = original
server.Close()
}
}

func TestAuthorize_PopulatesEmailWhenPresent(t *testing.T) {
t.Parallel()
a := assert.New(t)

cleanup := tokenServer(t, `{"access_token":"tok","expires_in":86400,"user_id":1,"email":"user@example.com"}`)
defer cleanup()

p := New("key", "secret", "/cb")
sess, err := p.BeginAuth("state")
a.NoError(err)
s := sess.(*Session)

token, err := s.Authorize(p, url.Values{"code": {"any"}})
a.NoError(err)
a.Equal("tok", token)
a.Equal("user@example.com", s.email)
}

func TestAuthorize_NoErrorWhenEmailMissing(t *testing.T) {
// Regression test for markbates/goth#338: VK accounts created without an
// email field omit "email" from the token response. The provider must
// treat it as optional and not fail the auth flow.
t.Parallel()
a := assert.New(t)

cleanup := tokenServer(t, `{"access_token":"tok","expires_in":86400,"user_id":1}`)
defer cleanup()

p := New("key", "secret", "/cb")
sess, err := p.BeginAuth("state")
a.NoError(err)
s := sess.(*Session)

token, err := s.Authorize(p, url.Values{"code": {"any"}})
a.NoError(err)
a.Equal("tok", token)
a.Equal("", s.email)
}
13 changes: 7 additions & 6 deletions providers/vk/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string,
return "", errors.New("Invalid token received from provider")
}

email, ok := token.Extra("email").(string)
if !ok {
return "", errors.New("Cannot fetch user email")
}

s.AccessToken = token.AccessToken
s.ExpiresAt = token.Expiry
s.email = email
// VK returns "email" as a top-level field in the token response only when the
// user has an email set in their profile AND has granted the email scope.
// Accounts registered without an email (allowed by VK since ~2018) omit it,
// so treat it as optional rather than failing the entire login flow (#338).
if email, ok := token.Extra("email").(string); ok {
s.email = email
}
return s.AccessToken, err
}

Expand Down