feat(auth): VK ID работает и как публичное приложение
Публичному приложению VK ID сервисный ключ не нужен: обмен кода защищает PKCE, и VK выдаёт токен по одному client_id. Раньше провайдер включался только при заполненных id и секрете, то есть публичный вариант был недоступен. - у провайдера появился флаг SecretOptional (у VK ID — true); - включение провайдера и каталог учитывают флаг; - тест TestOAuthVKPublicAppWithoutSecret: вход работает без секрета, в запросе обмена нет ни client_secret, ни service_token.
This commit is contained in:
+16
-3
@@ -60,6 +60,9 @@ type oauthProvider struct {
|
||||
// DeviceIDParam — имя query-параметра, который провайдер возвращает вместе с
|
||||
// кодом и требует при обмене кода на токен (у VK ID это device_id).
|
||||
DeviceIDParam string
|
||||
// SecretOptional — провайдер работает и без секрета (публичное приложение
|
||||
// VK ID: обмен защищён PKCE, сервисный ключ нужен только конфиденциальным).
|
||||
SecretOptional bool
|
||||
// TokenSecretParam — имя POST-параметра для секрета при обмене кода.
|
||||
// Пусто — секрет уходит обычной Basic-авторизацией (как у GitHub/Google).
|
||||
// У VK ID это service_token: он нужен конфиденциальным приложениям, а
|
||||
@@ -114,6 +117,7 @@ func defaultOAuthProviders() map[string]oauthProvider {
|
||||
// VK ID без PKCE не работает: code_challenge обязателен в запросе
|
||||
// авторизации, device_id возвращается в callback и нужен при обмене.
|
||||
PKCE: true,
|
||||
SecretOptional: true,
|
||||
DeviceIDParam: "device_id",
|
||||
TokenSecretParam: "service_token",
|
||||
FetchProfile: fetchVKProfile,
|
||||
@@ -171,7 +175,10 @@ func (s *Service) oauthConfigs() map[string]oauthProvider {
|
||||
enabled := make(map[string]oauthProvider, len(providers))
|
||||
for id, provider := range providers {
|
||||
credentials, ok := s.oauth[id]
|
||||
if !ok || credentials.clientID == "" || credentials.clientSecret == "" {
|
||||
if !ok || credentials.clientID == "" {
|
||||
continue
|
||||
}
|
||||
if credentials.clientSecret == "" && !provider.SecretOptional {
|
||||
continue
|
||||
}
|
||||
provider.Scopes = append([]string{}, provider.Scopes...)
|
||||
@@ -847,9 +854,15 @@ func oauthGetJSON(ctx context.Context, client *http.Client, token *oauth2.Token,
|
||||
func oauthFromConfig(cfg config.Config, baseURL string) (map[string]oauthClientCredentials, string) {
|
||||
credentials := map[string]oauthClientCredentials{}
|
||||
add := func(id, clientID, clientSecret string) {
|
||||
if clientID != "" && clientSecret != "" {
|
||||
credentials[id] = oauthClientCredentials{clientID: clientID, clientSecret: clientSecret}
|
||||
provider, known := oauthProviders()[id]
|
||||
if clientID == "" {
|
||||
return
|
||||
}
|
||||
// Публичному приложению VK ID секрет не нужен: обмен защищает PKCE.
|
||||
if clientSecret == "" && (!known || !provider.SecretOptional) {
|
||||
return
|
||||
}
|
||||
credentials[id] = oauthClientCredentials{clientID: clientID, clientSecret: clientSecret}
|
||||
}
|
||||
add("vk", cfg.OAuthVKClientID, cfg.OAuthVKClientSecret)
|
||||
add("yandex", cfg.OAuthYandexClientID, cfg.OAuthYandexClientSecret)
|
||||
|
||||
Reference in New Issue
Block a user