You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Native中获取Keycloak认证Token?

如何用Keycloak授权码换取可用于REST API的用户Token(React Native Expo场景)

问题背景

我首次使用Keycloak,正在为采用Keycloak做用户认证的网站开发React Native(Expo)应用。目前已完成登录并获取到如下成功响应:

{"authentication": null, "error": null, "errorCode": null, "params": {"code": "32c1d719-c8ce-4c1b-8800-5a853095da71.3fe7ff3e-feb2-47c9-b101-b24c90d596b4.918d5daa-5b42-4480-8832-3c88c5e876ad", "session_state": "3fe7ff3e-feb2-47c9-b101-b24c90d596b4", "state": "sVF3SdDAYg"}, "type": "success", "url": "exp://10.11.1.10:8081?state=sVF3SdDAYg&session_state=3fe7ff3e-feb2-47c9-b101-b24c90d596b4&code=32c1d719-c8ce-4c1b-8800-5a853095da71.3fe7ff3e-feb2-47c9-b101-b24c90d596b4.918d5daa-5b42-4480-8832-3c88c5e876ad"}

但不知如何获取可用于REST API的用户Token。我的登录代码如下:

import React, { useEffect, useState } from 'react';
import * as WebBrowser from 'expo-web-browser';
import { useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button, Text, View } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

export default function LoginPage() {
  const discovery = useAutoDiscovery('https://@@@@@@@@@@@@@@@@@@@@@@@@@');
  const redirectUri = 'exp://10.11.1.10:8081';

  const [accessToken, setAccessToken] = useState();
  const [request, result, promptAsync] = useAuthRequest(
    {
      clientId: 'mywebsite',
      redirectUri: redirectUri,
      scopes: ['openid', 'profile'],
      usePKCE: true,
    },
    discovery,); 

    console.log(result)

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button title="Login!" disabled={!request} onPress={() => promptAsync()} />
      {result && <Text>{JSON.stringify(result, null, 2)}</Text>}
    </View>
  );
}

我尝试使用Token REST API端点并传入密钥,但收到“no token”错误。


解决方案

你已经拿到了授权码(code),接下来需要用这个code去Keycloak的token端点换取access token、id token等凭证,以下是具体步骤:

1. 先确认Keycloak客户端配置

  • 确保客户端mywebsite开启了Authorization Code Flow(你用了PKCE,这个流程必须启用)
  • Valid Redirect URIs必须包含你的exp://10.11.1.10:8081
  • React Native属于公开客户端,需关闭Client Authentication选项(不需要客户端密钥)

2. 用授权码换取Token的代码实现

在result.type === 'success'的时机,调用Keycloak的token端点,注意必须传入PKCE所需的code_verifier:

import React, { useEffect, useState } from 'react';
import * as WebBrowser from 'expo-web-browser';
import { useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button, Text, View } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

export default function LoginPage() {
  // 替换为你的Keycloak realm地址
  const discovery = useAutoDiscovery('https://你的Keycloak域名/realms/你的realm名称');
  const redirectUri = 'exp://10.11.1.10:8081';

  const [accessToken, setAccessToken] = useState(null);
  const [request, result, promptAsync] = useAuthRequest(
    {
      clientId: 'mywebsite',
      redirectUri: redirectUri,
      scopes: ['openid', 'profile', 'email'], // 按需添加需要的权限 scope
      usePKCE: true,
    },
    discovery,
  ); 

  // 拿到授权码后请求Token
  useEffect(() => {
    if (result?.type === 'success') {
      const { code } = result.params;
      fetch(`${discovery.tokenEndpoint}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
          client_id: 'mywebsite',
          grant_type: 'authorization_code',
          code: code,
          redirect_uri: redirectUri,
          code_verifier: request.codeVerifier, // PKCE流程必需参数
        }),
      })
      .then(res => res.json())
      .then(data => {
        if (data.access_token) {
          setAccessToken(data.access_token);
          console.log('Access Token:', data.access_token);
          console.log('ID Token:', data.id_token);
          console.log('Refresh Token:', data.refresh_token);
        } else {
          console.error('获取Token失败:', data);
        }
      })
      .catch(err => console.error('请求错误:', err));
    }
  }, [result, request, discovery, redirectUri]);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
      <Button title="Login!" disabled={!request} onPress={() => promptAsync()} />
      {result && <Text style={{ marginTop: 20 }}>{JSON.stringify(result, null, 2)}</Text>}
      {accessToken && <Text style={{ marginTop: 20, color: 'green' }}>已获取到Access Token</Text>}
    </View>
  );
}

3. 常见错误排查

  • "no token"错误:你可能给公开客户端传入了client_secret,或者缺少code_verifier参数。公开客户端不需要密钥,且PKCE流程必须携带code_verifier。
  • 检查discovery是否正确加载:可以console.log(discovery)确认tokenEndpoint是否存在且地址正确。
  • 授权码有效期很短(Keycloak默认1分钟),必须在拿到code后立即发起换token请求。

4. Token的使用方式

拿到access_token后,在REST API请求的Authorization头中携带即可:

fetch('你的API接口地址', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
})

内容的提问来源于stack exchange,提问作者Tomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 17:43:18