Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update Yandex and VK OIDC #4158

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

renom
Copy link

@renom renom commented Oct 14, 2024

Changes

VK provider: Support PKCE

It seems like VK doesn't provide /.well-known/openid-configuration (VK doesn't support OpenID).
So to use PKCE it's needed to be enabled with:

pkce: "force"

VK provider: Support returning phone_number in claims

VK offers different endpoints for PKCE and non-PKCE configuration.
Token endpoint that is used in non-PKCE configuration can return email but not phone.
Extra endpoint https://id.vk.com/oauth2/user_info can be used to retrieve email and phone, but it doesn't accept access_token that was returned from non-PKCE token endpoint.
So to retrieve a phone, PKCE is required to be enabled:

  - id: vk
    provider: vk
    mapper_url: "data-mapper.jsonnet"
+   pkce: "force"
    scope:
      - email
+     - phone

Additional info:
I added AccessTokenURLOptions(r *http.Request) []oauth2.AuthCodeOption to OAuth2Provider interface.
It works like AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption but it's dedicated to update a token URL.
VK requires passing device_id URL param (that is received by a callback) to PKCE token endpoint. Without this change it's impossible.
I updated all the other OIDC providers to comply with the updated interface.

Yandex provider: Support returning phone_number in claims

No extra configuration is needed.

Related issue(s)

Fixes: #4147

Checklist

  • I have read the contributing guidelines.
  • I have referenced an issue containing the design document if my change
    introduces a new feature.
  • I am following the
    contributing code guidelines.
  • I have read the security policy.
  • I confirm that this pull request does not address a security
    vulnerability. If this pull request addresses a security vulnerability, I
    confirm that I got the approval (please contact
    [email protected]) from the maintainers to push
    the changes.
  • I have added tests that prove my fix is effective or that my feature
    works.
  • I have added or changed the documentation.

Further Comments

  • Everything is tested manually (including backward compatibility).
  • I didn't ask about approving the changes in Slack (please let me know if I need to).
  • VK seems to have removed the docs about non-PKCE endpoints, so PKCE endpoints is probably the recommended way to use OIDC.

Copy link
Member

@zepatrik zepatrik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good! Just one small question.

Comment on lines +162 to +163
if len(userInfo.User.Phone) > 0 && userInfo.User.Phone[0] != '+' {
userInfo.User.Phone = "+" + userInfo.User.Phone
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I'm not sure if this is the correct normalization to do here. I quickly checked VK docs, but couldn't find anything about the format.
If it is not clear, I'd prefer to do nothing instead and leave it to the consumer of the data.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All I've got is two examples from the docs, as well as a confirmation that it works that way (I've tested on my VK account that is registered with a Russian phone number). I didn't find any detailed description of the format either.
The examples from the docs:

  1. Example 1:
{
    "user": {
        "user_id": "1234567890", // Идентификатор пользователя.
        "first_name": "Ivan", // Имя пользователя.
        "last_name": "I.", // Первая буква фамилии пользвателя.
        "phone": "79903922415", // Телефон пользователя.
        "avatar": "https://pp.userapi.com/60tZWMo4SmwcploUVl9XEt8ufnTTvDUmQ6Bj1g/mmv1pcj63C4.png", // Ссылка на фото профиля пользователя.
        "email": "[email protected]", // Адрес почты пользователя.
        "sex": 2, // Пол. Возможные значения: Возможные значения: 1 — женский, 2 — мужской, 0 — пол не указан.
        "is_verified": false, // Признак того, что аккаунт пользователя подтвержден. Если это так, возвращается параметр true.
        "birthday": "01.01.2000" // Дата рождения пользователя.
    }
}
  1. Example 2:
{
    "user": {
        "user_id": "1234567890",
        "first_name": "Ivan",
        "last_name": "I.",
        "phone": "79991234567",
        "avatar": "http://avatar.com/12345678",
        "email": "[email protected]",
        "sex": 2,
        "verified": false,
        "birthday": "01.01.2000"
    }
}

79903922415 and 79991234567 are Russian phone numbers in the international format, but without a leading + sign.
As far as I understand, Ory Kratos uses international format, but with a leading + sign.
I'm not sure whether all the phones are returned in the same format or not. For example I can't test with non-Russian phones. This is why I added condition if there's no plus sign, then add it.
If you insist, I can remove that code block (lines 162-164). It shouldn't be the problem to do the same action within a .jsonnet file though.

Copy link
Contributor

@hperl hperl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as well! I just wonder if we can enable PKCE for VK by default?

@@ -27,6 +27,7 @@ type Provider interface {
type OAuth2Provider interface {
Provider
AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption
AccessTokenURLOptions(r *http.Request) []oauth2.AuthCodeOption
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this method is only relevant for VK, we could also extract it into a separate interface that only the VK provider implements. Then, in strategy.go we assert on the interface and add the options if the provider implements it.

That way, we don't have to give empty implementations to every other provider. WDYT?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add a new interface, but I need some directions. How to name this new interface?
I used the same interface because AuthCodeURLOptions isn't in a separate interface either (there's empty implementations for this method too).

Endpoint: oauth2.Endpoint{
func (p *ProviderVK) oauth2(ctx context.Context) *oauth2.Config {
var endpoint oauth2.Endpoint
if p.config.PKCE == "force" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason / use case for not using PKCE with VK? Ideally, we would use PKCE by default for all providers that support it.

The explicit config flag for PKCE should only be necessary for the generic provider.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can enable PKCE for pkce == "auto" too. I thought auto should enable PKCE only if /.well-known/openid-configuration returns that it's supported, that's why I didn't enable PKCE for auto.
I don't know the reason why we shouldn't use PKCE. But enabling non-PKCE configuration when pkce is explicitly set to never seems logical.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just remembered the reason why PKCE shouldn't be enabled by default.
Ory Kratos uses different URLs for PKCE and non-PKCE callbacks:

  • /self-service/methods/oidc/callback/vk (PKCE disabled)
  • /self-service/methods/oidc/callback (PKCE enabled)

If we enable PKCE by default, the clients that were created before this PR is merged won't work (because they were created with URL /self-service/methods/oidc/callback/vk)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Yandex/vk oidc: prefill phone number in claims
3 participants