当后端在 Docker 容器中 运行 时,Keycloak 令牌验证失败
Keycloak token verification fails when the backend is running in a Docker container
我正处于构建 Web 应用程序的早期阶段。我打算使用 Keycloak 作为身份提供者来保护后端。在我的本地机器上,我 运行 Keycloak 和我的后端都是 docker 容器,但在不同的网络上,因为最终在生产中,我想要认证服务器 运行 Keycloak 运行 与后端分开,例如 account.example.com
和 api.example.com
分别
在本地,我的 Keycloak 容器可以通过基础 URL http://localhost:8080/auth
和后端通过 http://test.localhost:8000/
访问
我在 Keycloak 领域创建了一个客户端,其访问类型是保密的。我正在使用授权代码授予类型生成令牌。
因此,后端的每个 REST API 端点都会验证传递给授权的令牌 header,然后在处理请求之前调用 Keycloak 服务器来验证令牌。
我目前遇到的问题是令牌验证失败并响应
{"error":"invalid_token","error_description":"Token verification failed"}'
经过调查,显然是因为我从后端 API 容器调用 Keycloak 服务器。如果我在后端 docker 容器中使用 curl 生成令牌,我收到的令牌被验证正常,但在容器外部生成的令牌却没有。
我正在使用 python-keycloak
作为 Keycloak REST 的包装器 API
from keycloak import KeycloakOpenID
self._keycloak = KeycloakOpenID(
server_url='http://host.docker.internal:8080/auth/',
realm_name='myrealm',
client_id='myclient',
client_secret_key='mysecret,
)
if "HTTP_AUTHORIZATION" not in request.META:
return JsonResponse(
{"detail": NotAuthenticated.default_detail},
status=NotAuthenticated.status_code,
)
auth_header = request.META.get("HTTP_AUTHORIZATION").split()
token = auth_header[1] if len(auth_header) == 2 else auth_header[0]
try:
self.keycloak.userinfo(token)
except KeycloakInvalidTokenError as e:
# print(e)
return JsonResponse(
{"detail": AuthenticationFailed.default_detail},
status=AuthenticationFailed.status_code,
)
如何解决此问题并在我的本地计算机上进行令牌验证
问题出在令牌的 issuer
上。邮递员的令牌颁发者是 http://localhost:8080/...
,但后端配置为仅接受颁发者 http://host.docker.internal:8080/...
。最佳做法是在任何地方对 IdP(在您的情况下为 Keycloak)使用相同的 protocol:domain[:port]
,例如https://keycloak.domain.com
,否则就会出现这种问题
您的令牌无效,因为令牌中的颁发者 (iss
) 与您的后端服务期望的颁发者不匹配。
您的后端(或后端中的 adapter/framework)将使用 OIDC 发现协议来确定预期的发行者。为此,它将调用 https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration
。这将 return 像这样的元数据:
{
"issuer":"https://keycloak-container-name/auth/realms/<your-realm>",
...
}
Keycloak 将根据请求确定颁发者的主机部分(在本例中为keycloak-container-name
)。因此,如果您的后端从 docker 网络中使用 keycloak-container-name
查询发现端点,则主机部分将为 your-container-name
。在这种情况下,您的后端将期望发行人为 https://keycloak-container-name/auth/realms/<your-realm>
。
现在,如果您想从前端查询令牌,您的前端会将请求发送到 http://localhost:8080/auth/...
。由于发行人将根据请求确定,因此在这种情况下,该令牌中的发行人将为 https://localhost:8080/auth/realms/<your-realm>
。
这与预期的发行者不匹配 https://keycloak-container-name/auth/realms/<your-realm>
,因此令牌将被拒绝为无效。
您还可以通过 http://localhost:8080/auth/realms/<your-realm>/.well-known/openid-configuration
调用 OIDC 发现端点来验证这一点。您将收到这样的回复:
{
"issuer":"http://localhost:8080/auth/realms/<your-realm>",
...
}
要解决此问题,您可以将您领域中的 Frontend URL
设置为 http://localhost:8080/auth
。使用此设置,发行者将不再在请求中确定,而是固定 http://localhost:8080/auth/realms/<your-realm>
。
您可以通过向 https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration
发出请求从您的后端容器中检查这一点。现在 return 元数据如下:
{
"issuer":"http://localhost:8080/auth/realms/<your-realm>",
...
}
如果您不想为每个领域单独配置它,您可以改为配置 default hostname provider 服务器范围。
我正处于构建 Web 应用程序的早期阶段。我打算使用 Keycloak 作为身份提供者来保护后端。在我的本地机器上,我 运行 Keycloak 和我的后端都是 docker 容器,但在不同的网络上,因为最终在生产中,我想要认证服务器 运行 Keycloak 运行 与后端分开,例如 account.example.com
和 api.example.com
分别
在本地,我的 Keycloak 容器可以通过基础 URL http://localhost:8080/auth
和后端通过 http://test.localhost:8000/
我在 Keycloak 领域创建了一个客户端,其访问类型是保密的。我正在使用授权代码授予类型生成令牌。
因此,后端的每个 REST API 端点都会验证传递给授权的令牌 header,然后在处理请求之前调用 Keycloak 服务器来验证令牌。
我目前遇到的问题是令牌验证失败并响应
{"error":"invalid_token","error_description":"Token verification failed"}'
经过调查,显然是因为我从后端 API 容器调用 Keycloak 服务器。如果我在后端 docker 容器中使用 curl 生成令牌,我收到的令牌被验证正常,但在容器外部生成的令牌却没有。
我正在使用 python-keycloak
作为 Keycloak REST 的包装器 API
from keycloak import KeycloakOpenID
self._keycloak = KeycloakOpenID(
server_url='http://host.docker.internal:8080/auth/',
realm_name='myrealm',
client_id='myclient',
client_secret_key='mysecret,
)
if "HTTP_AUTHORIZATION" not in request.META:
return JsonResponse(
{"detail": NotAuthenticated.default_detail},
status=NotAuthenticated.status_code,
)
auth_header = request.META.get("HTTP_AUTHORIZATION").split()
token = auth_header[1] if len(auth_header) == 2 else auth_header[0]
try:
self.keycloak.userinfo(token)
except KeycloakInvalidTokenError as e:
# print(e)
return JsonResponse(
{"detail": AuthenticationFailed.default_detail},
status=AuthenticationFailed.status_code,
)
如何解决此问题并在我的本地计算机上进行令牌验证
问题出在令牌的 issuer
上。邮递员的令牌颁发者是 http://localhost:8080/...
,但后端配置为仅接受颁发者 http://host.docker.internal:8080/...
。最佳做法是在任何地方对 IdP(在您的情况下为 Keycloak)使用相同的 protocol:domain[:port]
,例如https://keycloak.domain.com
,否则就会出现这种问题
您的令牌无效,因为令牌中的颁发者 (iss
) 与您的后端服务期望的颁发者不匹配。
您的后端(或后端中的 adapter/framework)将使用 OIDC 发现协议来确定预期的发行者。为此,它将调用 https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration
。这将 return 像这样的元数据:
{
"issuer":"https://keycloak-container-name/auth/realms/<your-realm>",
...
}
Keycloak 将根据请求确定颁发者的主机部分(在本例中为keycloak-container-name
)。因此,如果您的后端从 docker 网络中使用 keycloak-container-name
查询发现端点,则主机部分将为 your-container-name
。在这种情况下,您的后端将期望发行人为 https://keycloak-container-name/auth/realms/<your-realm>
。
现在,如果您想从前端查询令牌,您的前端会将请求发送到 http://localhost:8080/auth/...
。由于发行人将根据请求确定,因此在这种情况下,该令牌中的发行人将为 https://localhost:8080/auth/realms/<your-realm>
。
这与预期的发行者不匹配 https://keycloak-container-name/auth/realms/<your-realm>
,因此令牌将被拒绝为无效。
您还可以通过 http://localhost:8080/auth/realms/<your-realm>/.well-known/openid-configuration
调用 OIDC 发现端点来验证这一点。您将收到这样的回复:
{
"issuer":"http://localhost:8080/auth/realms/<your-realm>",
...
}
要解决此问题,您可以将您领域中的 Frontend URL
设置为 http://localhost:8080/auth
。使用此设置,发行者将不再在请求中确定,而是固定 http://localhost:8080/auth/realms/<your-realm>
。
您可以通过向 https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration
发出请求从您的后端容器中检查这一点。现在 return 元数据如下:
{
"issuer":"http://localhost:8080/auth/realms/<your-realm>",
...
}
如果您不想为每个领域单独配置它,您可以改为配置 default hostname provider 服务器范围。