expo 项目, 实现 okta 登录(Typescript)

expo 项目, 实现 okta 登录(Typescript)

Button, Text 组件引用于 React Native Elements

expo 使用 okta 的官方文档的使用说明, docs.expo.dev/guides/auth...

官方 code 示例:

javascript 复制代码
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button, Platform } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

export default function App() {
  // Endpoint
  const discovery = useAutoDiscovery('https://<OKTA_DOMAIN>.com/oauth2/default');
  // Request
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['openid', 'profile'],
      redirectUri: makeRedirectUri({
        native: 'com.okta.<OKTA_DOMAIN>:/callback',
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}

这里的针对自己的项目需要做些调整与解释:

  1. clientId, scopes, redirectUri, discovery
  2. 返回 code 之后, 怎么使用? 怎么拿到 accessToken
  • 设置 .env 存放敏感配置
config 复制代码
EXPO_PUBLIC_OKTA_CLIENT_ID=0oaxxxxxxxxxxxxxxxxx
EXPO_PUBLIC_OKTA_ISSUER=https://<my_company_name>.okta.com/oauth2/ausxxxxxxxxxxxxxxxxx
EXPO_PUBLIC_OKTA_CALLBACK=<my_company_name>-data://callback

注意: CLIENT_ID 一般是 0oaxxxxxxxxxxxxxxxxx 开头, 20 位

  • 直接使用

注意:

  1. redirectUri 是直接使用 process.env.EXPO_PUBLIC_OKTA_CALLBACK 并没有调用 makeRedirectUri
  2. scopes 可能根据个人需要调整
  3. const discovery = useAutoDiscovery(oktaConfig.issuerUrl); 会在 useAuthRequestexchangeCodeAsync 中用到. discovery 的类型是 DiscoveryDocument

codes:

js 复制代码
import { Button, Text } from "@rneui/themed";
import {
  DiscoveryDocument,
  TokenResponse,
  TokenResponseConfig,
  exchangeCodeAsync,
  useAuthRequest,
  useAutoDiscovery,
} from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import * as React from "react";
import { ScrollView, StyleSheet, View } from "react-native";

WebBrowser.maybeCompleteAuthSession();

// okta configuration
const oktaConfig = {
  clientId: process.env.EXPO_PUBLIC_OKTA_CLIENT_ID as string,
  scopes: ["openid", "profile", "email", "groups"],
  issuerUrl: process.env.EXPO_PUBLIC_OKTA_ISSUER as string,
  callbackUrl: process.env.EXPO_PUBLIC_OKTA_CALLBACK as string,
};

export default function App() {
  // Endpoint
  const discovery = useAutoDiscovery(oktaConfig.issuerUrl);
  // Request
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: oktaConfig.clientId,
      scopes: oktaConfig.scopes,
      redirectUri: oktaConfig.callbackUrl,
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === "success") {
      const { code } = response.params;

      if (request && code) {
        // function for retrieving the access token and refresh token from our code
        const getToken = async () => {
          const codeRes: TokenResponse = await exchangeCodeAsync(
            {
              code,
              redirectUri: oktaConfig.callbackUrl,
              extraParams: request.codeVerifier
                ? { code_verifier: request.codeVerifier }
                : undefined,
              clientId: oktaConfig.clientId,
            },
            discovery as DiscoveryDocument
          );
          // get the config from our response to cache for later refresh
          const tokenConfig: TokenResponseConfig = codeRes?.getRequestConfig();

          // get the access token to use
          const jwtToken = tokenConfig.accessToken;

          // caching the token for later
          console.log("jwtToken: ", jwtToken);

          // decoding the token for getting user profile information
        };
        getToken();
      }
    }
  }, [response]);

  return (
    <View style={styles.container}>
      <ScrollView>
        {response && <Text>{JSON.stringify(response, null, 2)}</Text>}
      </ScrollView>
      <Button
        title="Log in"
        loading={false}
        loadingProps={{ size: "small", color: "white" }}
        buttonStyle={{
          backgroundColor: "rgba(111, 202, 186, 1)",
          borderRadius: 5,
        }}
        titleStyle={{ fontWeight: "bold", fontSize: 23 }}
        containerStyle={{
          marginHorizontal: 50,
          height: 50,
          width: 200,
          marginVertical: 10,
        }}
        onPress={() => {
          promptAsync();
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    margin: 10,
    marginTop: 20,
  },
});

参考:

希望对 expo 开发, 对接 okta 的开发者有帮助, saved your time.

相关推荐
2601_949593652 小时前
基础入门 React Native 鸿蒙跨平台开发:卡片组件
react native·react.js·harmonyos
qq_177767373 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_177767373 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体
烬头88214 小时前
React Native鸿蒙跨平台采用了函数式组件的形式,通过 props 接收分类数据,使用 TouchableOpacity实现了点击交互效果
javascript·react native·react.js·ecmascript·交互·harmonyos
qq_177767374 小时前
React Native鸿蒙跨平台通过Animated.Value.interpolate实现滚动距离到动画属性的映射
javascript·react native·react.js·harmonyos
●VON5 小时前
React Native for OpenHarmony:项目目录结构与跨平台构建流程详解
javascript·学习·react native·react.js·架构·跨平台·von
qq_177767375 小时前
React Native鸿蒙跨平台实现消息列表用于存储所有消息数据,筛选状态用于控制消息筛选结果
javascript·react native·react.js·ecmascript·harmonyos
摘星编程6 小时前
React Native + OpenHarmony:自定义useEllipsis省略号处理
javascript·react native·react.js
2601_949593657 小时前
基础入门 React Native 鸿蒙跨平台开发:Animated 动画按钮组件 鸿蒙实战
react native·react.js·harmonyos
●VON8 小时前
React Native for OpenHarmony:ScrollView 事件流、布局行为与性能优化深度剖析
学习·react native·react.js·性能优化·openharmony