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

{"error":"invalid_request","error_description":"verifier is missing or invalid"} #34

Open
afarber opened this issue Feb 4, 2025 · 2 comments

Comments

@afarber
Copy link

afarber commented Feb 4, 2025

Здравствуйте, помогите, пожалуйста, с моим кодом, кажется, не хватает мелочи.

Моя конечная цель получить user id, first name, photo через redirect для словесной игры
(Google FedCM, Apple Sign in, Huawei, Amazon уже успешно прикручены).

        <script src="https://unpkg.com/@vkid/[email protected]/dist-sdk/umd/index.js"></script>
        <script>
            if ('VKIDSDK' in window) {
                const VKID = window.VKIDSDK;

                VKID.Config.init({
                    app: 53002778,
                    redirectUrl: 'https://wordsbyfarber.com/ru/vk',
                    state: '4e6468fb1550b1fd',
                    codeChallenge: 'iTOc_ymECdVgTN9zAsI_gy8j-jX-LsjcE5CoOG4MCp0',
                    responseMode: VKID.ConfigResponseMode.Redirect
                });

                const oneTap = new VKID.OneTap();

                oneTap.render({
                    container: document.currentScript.parentElement,
                    showAlternativeLogin: true,
                    oauthList: [
                        'mail_ru',
                        'ok_ru'
                    ]
                })
                .on(VKID.WidgetEvents.ERROR, vkidOnError)
                .on(VKID.OneTapInternalEvents.LOGIN_SUCCESS, function (payload) {
                    const code = payload.code;
                    const deviceId = payload.device_id;

                    VKID.Auth.exchangeCode(code, deviceId)
                        .then(vkidOnSuccess)
                        .catch(vkidOnError);
                });

                function vkidOnSuccess(data) {
                    console.log('vkidOnSuccess', data);
                    VKID.Auth.userInfo(data.access_token)
                        .then(function (userInfo) {
                            console.log('userInfo', userInfo);
                        })
                        .catch(function (error) {
                            console.error('userInfo', error);
                        });
                }

                function vkidOnError(error) {
                    console.error('vkidOnError', error);
                }
            }
        </script>

И вот код моего Jakarta EE10 servlet, использует Jetty 12 http client:

    private void handleVkAuth(HttpServletRequest httpReq, HttpServletResponse httpResp)
            throws ServletException, IOException {

        String nonce = generateNonce(httpReq);
        String codeVerifier = generateCodeVerifier();
        String codeChallenge = generateCodeChallenge(codeVerifier);

        String state = httpReq.getParameter("state");
        String code = httpReq.getParameter("code");
        String expires_in = httpReq.getParameter("expires_in");
        String device_id = httpReq.getParameter("device_id");
        String type = httpReq.getParameter("type");

        if (code == null ||
                code.length() < 60 ||
                !nonce.equals(state)) {

            String html = """
                    <html>
                        <body>
                            <div>
                            <script src="https://unpkg.com/@vkid/[email protected]/dist-sdk/umd/index.js"></script>
                            <script>
                                if ('VKIDSDK' in window) {
                                    const VKID = window.VKIDSDK;

                                    VKID.Config.init({
                                        app: %s,
                                        redirectUrl: '%s',
                                        state: '%s',
                                        codeChallenge: '%s',
                                        responseMode: VKID.ConfigResponseMode.Redirect
                                    });

                                    const oneTap = new VKID.OneTap();

                                    oneTap.render({
                                        container: document.currentScript.parentElement,
                                        showAlternativeLogin: true,
                                        oauthList: [
                                            'mail_ru',
                                            'ok_ru'
                                        ]
                                    })
                                    .on(VKID.WidgetEvents.ERROR, vkidOnError)
                                    .on(VKID.OneTapInternalEvents.LOGIN_SUCCESS, function (payload) {
                                        const code = payload.code;
                                        const deviceId = payload.device_id;

                                        VKID.Auth.exchangeCode(code, deviceId)
                                            .then(vkidOnSuccess)
                                            .catch(vkidOnError);
                                    });

                                    function vkidOnSuccess(data) {
                                        console.log('vkidOnSuccess', data);
                                        VKID.Auth.userInfo(data.access_token)
                                            .then(function (userInfo) {
                                                console.log('userInfo', userInfo);
                                            })
                                            .catch(function (error) {
                                                console.error('userInfo', error);
                                            });
                                    }

                                    function vkidOnError(error) {
                                        console.error('vkidOnError', error);
                                    }
                                }
                            </script>
                            </div>
                        </body>
                    </html>
                            """.formatted(
                    VK_CLIENT_ID,
                    String.format(VK_REDIRECT_URI, mLanguage),
                    nonce,
                    codeChallenge);

            httpResp.setStatus(HttpServletResponse.SC_OK);
            httpResp.setContentType(TEXT_HTML);
            httpResp.getWriter().print(html);
            return;
        }

        try {
            MultiMap<String> tokenBodyMap = new MultiMap<>();
            tokenBodyMap.add("grant_type", "authorization_code");
            tokenBodyMap.add("code", code);
            tokenBodyMap.add("device_id", device_id);
            tokenBodyMap.add("client_id", VK_CLIENT_ID);
            tokenBodyMap.add("code_verifier", codeVerifier);
            tokenBodyMap.add("redirect_uri", String.format(VK_REDIRECT_URI, mLanguage));

            String postParams = UrlEncoded.encode(tokenBodyMap, StandardCharsets.UTF_8, false);

            String jsonResponse = mHttpClient.POST("https://id.vk.com/oauth2/auth")
                    .headers(httpFields -> {
                        httpFields.add(new HttpField(HttpHeader.ACCEPT, APPLICATION_JSON));
                        httpFields.add(new HttpField(HttpHeader.CONTENT_TYPE, APPLICATION_URLENCODED));
                    })
                    .body(new StringRequestContent(postParams))
                    .send()
                    .getContentAsString();

            httpResp.setStatus(HttpServletResponse.SC_OK);
            httpResp.setContentType(APPLICATION_JSON);
            httpResp.getWriter().print(jsonResponse);
        } catch (Exception ex) {
            ex.printStackTrace();
            httpResp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            httpResp.getWriter().println("Error");
        }
    }

    private String generateCodeVerifier() {
        String validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
        SecureRandom secureRandom = new SecureRandom();
        StringBuilder codeVerifier = new StringBuilder(128);
        for (int i = 0; i < 128; i++) {
            codeVerifier.append(validChars.charAt(secureRandom.nextInt(validChars.length())));
        }
        return codeVerifier.toString();
    }

    private String generateCodeChallenge(String codeVerifier) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII));
            return Base64.getUrlEncoder().withoutPadding().encodeToString(hash);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 algorithm not available.");
        }
    }

Почему-то запрос POST к адресу "https://id.vk.com/oauth2/auth" возвращает ответ

{"error":"invalid_request","error_description":"verifier is missing or invalid"}
@afarber
Copy link
Author

afarber commented Feb 4, 2025

И вот мои логи:

handleVkAuth nonce = cf3a6acedf350030364f40f125542bde codeVerifier = b6hkMM4GiFr61hY8_U5kHsJOTqaLfFGCu0x_O_yH6ZjawZKrafbKrXtiIo8_LXISztZpoyAi5LO1LGTAaS5qLCfpxdbnXKgy1eeUVD_merfUMw0xHLzdrfxpOPnm2Txc codeChallenge = iTOc_ymECdVgTN9zAsI_gy8j-jX-LsjcE5CoOG4MCp0

handleVkAuth postParams = grant_type=authorization_code&code=vk2.a.XfxXyAdRN4fmcIL2wP-YhMQX6OmZnZRrDnJRA3-s1rydVsEHUpuydC7nsSeB77TcUwCDXgTBWD9F6vDPfm2MAM0zEbr9QHjYImUYtR94JE7Z-TJ11cc70i9YFYbf475U1X86Isvc_9cG44iLiBo-x0D5iACeOwO1HWhood3NLOG5T0929RYz5ipdGOMrzTf5d69xGsoN2PN3RV4JgDWsPA&device_id=Kj7rS4is6QVxmt5B-wlWojwRdCDLR8vtXH8BH1rYtwKhnsZyOHz_OtAhp5UopmDOsbmbePDv7qWcARC4XPqquw&client_id=53002778&code_verifier=b6hkMM4GiFr61hY8_U5kHsJOTqaLfFGCu0x_O_yH6ZjawZKrafbKrXtiIo8_LXISztZpoyAi5LO1LGTAaS5qLCfpxdbnXKgy1eeUVD_merfUMw0xHLzdrfxpOPnm2Txc&redirect_uri=https%3A%2F%2Fwordsbyfarber.com%2Fru%2Fvk

handleVkAuth jsonResponse = {"error":"invalid_request","error_description":"verifier is missing or invalid"}

Мои codeVerifier и codeChallenge я проверил на https://tonyxu-io.github.io/pkce-generator/ и они совпадают:

Image

@afarber
Copy link
Author

afarber commented Feb 4, 2025

Также я сравнивал с Вашим тестом и вроде бы делаю в моем Java коде то же самое -

Image

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

No branches or pull requests

1 participant