Вопрос или проблема
Во-первых, я тщательно искал ответ в Google и проверял обсуждения на форумах. Попробовал все, что нашел, но ничего не сработало. Вот я здесь.
Сначала о настройках.
Я запускаю все локально. Я использую Docker для Keycloak и саморазмещенную версию Supabase.
Настройки клиента Keycloak:
Допустимые URI перенаправления: http://localhost:3000/auth/callback; http://localhost:8000/auth/v1/callback
Web Origins: http://localhost:3000; http://localhost:8000
Аутентификация клиента: true
Docker-compose.yml для Supabase
GOTRUE_EXTERNAL_KEYCLOAK_ENABLED: "true"
GOTRUE_EXTERNAL_KEYCLOAK_CLIENT_ID: "supabase"
GOTRUE_EXTERNAL_KEYCLOAK_SECRET: "0L3PRTgpQNx4hnkdamvjQYg8PiZqbS5x"
GOTRUE_EXTERNAL_KEYCLOAK_URL: "http://localhost:8080/realms/master"
GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: "http://localhost:3000/auth/callback"
Переменные среды docker-compose для Supabase
ADDITIONAL_REDIRECT_URLS="http://localhost:3000/auth/callback"
Я использую шаблон Supabase для Nextjs.
Я добавил следующее в файл действий.
export const signInwithKeycloak = async () => {
const supabase = createClient();
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "keycloak",
options: {
scopes: "openid",
redirectTo: "http://localhost:3000/auth/callback",
},
});
if (error) {
console.error("Ошибка OAuth:", error.message);
return encodedRedirect("error", "/sign-in", error.message);
}
console.log("Данные ответа OAuth из действий:", data); // Логируем данные ответа, чтобы убедиться, что вы получаете правильный URL
return redirect(data.url); // Убедитесь, что это перенаправляет корректно
};
В auth/callback/route.ts
import { createClient } from "@/utils/supabase/server";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get("code");
const origin = requestUrl.origin;
const redirectTo = requestUrl.searchParams.get("redirect_to")?.toString();
const supabase = createClient();
if (code) {
console.log("Код авторизации найден в URL:", code);
// Пытаемся обменять код авторизации на сессию
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
console.log("данные из callback", data);
if (error) {
console.error("Ошибка обмена кода на сессию:", error.message);
return NextResponse.redirect(
`${origin}/sign-in?error=${encodeURIComponent(error.message)}`
);
}
} else {
console.error("Код авторизации не найден в URL.");
return NextResponse.redirect(
`${origin}/sign-in?error=Код авторизации отсутствует`
);
}
if (redirectTo) {
return NextResponse.redirect(`${origin}${redirectTo}`);
}
return NextResponse.redirect(`${origin}/protected`);
}
На странице входа я просто добавил кнопку и установил соответствующий formAction.
Когда я нажимаю “Войти”, затем кнопку “войти с помощью Keycloak”, я перенаправляюсь на страницу входа Keycloak. Когда я успешно вхожу, меня перенаправляют на страницу входа приложения с ошибкой “некорректное состояние потока, состояние потока не найдено”.
Когда я проверяю куки, я вижу “sb-localhost-auth-token-code-verifier”.
Когда я проверяю сетевую активность:
URL запроса:
http://localhost:8000/auth/v1/authorize?provider=keycloak&redirect_to=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fcallback&scopes=openid&code_challenge=Ayzoh0qqlZK9q_BPQRZ4-ao28xkH4C7tvtpuOI3ptFU&code_challenge_method=s256
Данные
provider: keycloak
redirect_to: http://localhost:3000/auth/callback
scopes: openid
code_challenge: Ayzoh0qqlZK9q_BPQRZ4-ao28xkH4C7tvtpuOI3ptFU
code_challenge_method: s256
http://localhost:8080/realms/master/protocol/openid-connect/auth?client_id=supabase&redirect_to=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fcallback&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fcallback&response_type=code&scope=profile+email+openid&state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjgxMDQxODcsInNpdGVfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwiaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCJmdW5jdGlvbl9ob29rcyI6bnVsbCwicHJvdmlkZXIiOiJrZXljbG9hayIsInJlZmVycmVyIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwL2F1dGgvY2FsbGJhY2siLCJmbG93X3N0YXRlX2lkIjoiMzgyZDFhYTEtMzNmMi00NWM3LWI5NGItMzk3YTg5YTBlMjgxIn0.wy5N_hLNJqvh_YNBDrFeuea92KeOdL_ir0eHWwqYz9I
Данные
client_id: supabase
redirect_to: http://localhost:3000/auth/callback
redirect_uri: http://localhost:3000/auth/callback
response_type: code
scope: profile email openid
state: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjgxMDQxODcsInNpdGVfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwiaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCJmdW5jdGlvbl9ob29rcyI6bnVsbCwicHJvdmlkZXIiOiJrZXljbG9hayIsInJlZmVycmVyIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwL2F1dGgvY2FsbGJhY2siLCJmbG93X3N0YXRlX2lkIjoiMzgyZDFhYTEtMzNmMi00NWM3LWI5NGItMzk3YTg5YTBlMjgxIn0.wy5N_hLNJqvh_YNBDrFeuea92KeOdL_ir0eHWwqYz9I
http://localhost:3000/auth/callback?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjgxMDQxODcsInNpdGVfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwiaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCJmdW5jdGlvbl9ob29rcyI6bnVsbCwicHJvdmlkZXIiOiJrZXljbG9hayIsInJlZmVycmVyIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwL2F1dGgvY2FsbGJhY2siLCJmbG93X3N0YXRlX2lkIjoiMzgyZDFhYTEtMzNmMi00NWM3LWI5NGItMzk3YTg5YTBlMjgxIn0.wy5N_hLNJqvh_YNBDrFeuea92KeOdL_ir0eHWwqYz9I&session_state=181a8fe2-2bf5-4279-8bfd-c8880d89c51c&iss=http%3A%2F%2Flocalhost%3A8080%2Frealms%2Fmaster&code=03399bd7-f454-4e89-bd7d-e04eec6908d4.181a8fe2-2bf5-4279-8bfd-c8880d89c51c.b37b3d25-c578-4ad6-afa8-03f6a6bbae02
Данные
state: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjgxMDQxODcsInNpdGVfdXJsIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwiaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCJmdW5jdGlvbl9ob29rcyI6bnVsbCwicHJvdmlkZXIiOiJrZXljbG9hayIsInJlZmVycmVyIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwL2F1dGgvY2FsbGJhY2siLCJmbG93X3N0YXRlX2lkIjoiMzgyZDFhYTEtMzNmMi00NWM3LWI5NGItMzk3YTg5YTBlMjgxIn0.wy5N_hLNJqvh_YNBDrFeuea92KeOdL_ir0eHWwqYz9I
session_state: 181a8fe2-2bf5-4279-8bfd-c8880d89c51c
iss: http://localhost:8080/realms/master
code: 03399bd7-f454-4e89-bd7d-e04eec6908d4.181a8fe2-2bf5-4279-8bfd-c8880d89c51c.b37b3d25-c578-4ad6-afa8-03f6a6bbae02
Если вам нужна дополнительная информация, пожалуйста, дайте знать. Я старался сохранить как можно больше значений по умолчанию.
Ответ или решение
Проблема, с которой вы столкнулись, связана с ошибкой "Invalid flow state, no valid flow state found" при использовании Keycloak в качестве провайдера OAuth с Supabase. Давайте разберем возможные причины возникновения этой ошибки и шаги по её устранению.
1. Проверьте настройки Keycloak
Убедитесь, что вы правильно настроили клиент в Keycloak. Следующие пункты могут быть важны:
- Valid Redirect URIs: Убедитесь, что вы используете правильные URI. Поскольку у вас есть несколько окружений (localhost:3000 и localhost:8000), убедитесь, что оба указаны корректно и совпадают с тем, что используется в приложении.
- Web Origins: Убедитесь, что в этом поле указаны все нужные origins, например
http://localhost:3000
иhttp://localhost:8000
.
2. Проверьте конфигурацию Supabase
Ваши параметры в файле docker-compose.yml
выглядят корректными, но обратите внимание на следующее:
- GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: Убедитесь, что этот URI точно совпадает с одним из указанных в Keycloak. Если в Keycloak указано
http://localhost:3000/auth/callback
, то эта строка должна совпадать.
3. Коды и состояние (state)
При обмене кода на сессию в вашей функции GET
в auth/callback/route.ts
необходимо убедиться, что состояние (state
), отправленное на сервер Keycloak, совпадает с тем состоянием, которое вы получили при возврате:
- Проверьте, что
code_verifier
иcode_challenge
правильно генерируются и хранятся в cookie, чтобы они могли быть использованы при обмене кода.
4. Проверьте обработку состояния
Когда вы переводите запрос на сервер Keycloak, проверьте, правильно ли вы обрабатываете поле state
:
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
Проверьте логи или отладочную информацию, чтобы понять, корректно ли передается состояние и совпадает ли оно с правильным значением.
5. Ответы на запросы
При проверке сетевой активности, обратите внимание на следующее:
- Запросы к
authorize
иtoken
должны корректно указыватьstate
, и это состояние должно храниться между запросами.
6. Другие соображения
- Попробуйте использовать другой браузер или режим инкогнито, чтобы избежать кэширования или старых cookies.
- Проверьте версии используемых библиотек (
@supabase/supabase-js
, Keycloak и т.д.), так как иногда ошибки могут быть связаны с несовместимостью версий.
7. Логи и отладка
Соберите и проанализируйте логи как Keycloak, так и Supabase, чтобы выявить дополнительные подсказки. Если возникнут повторные ошибки, возможно, стоит включить дополнительную отладочную информацию.
Если все вышеперечисленное не помогает решить проблему, вы можете рассмотреть возможность полной переустановки окружения или обращения к сообществу Supabase и Keycloak за поддержкой, предоставив все детали конфигурации.
В случае возникновения дополнительных вопросов или необходимости в помощи, пожалуйста, дайте знать!