简体   繁体   English

Okta认证,response.params如何使用?

[英]Okta authentication, how to use the response.params?

We are trying to use Expo authentication with Okta as stated here:我们正在尝试使用 Okta 的 Expo 身份验证,如下所述:

https://docs.expo.dev/guides/authentication/#okta https://docs.expo.dev/guides/authentication/#okta

Expo has very good documentation for lot's of stuff, but for the Okta authentication unfortunately we could not sort out how to use the library in a correct way. Expo 有很多很好的文档,但不幸的是,对于 Okta 身份验证,我们无法弄清楚如何以正确的方式使用该库。

Currently, with lot's of suffering (mostly because of the ambiguity in Okta's configuration pages), we came to a certain point where the following code correctly responds the code parameter.目前,我们遇到了很多麻烦(主要是因为 Okta 配置页面中的歧义),我们来到了一个特定的点,下面的代码正确地响应了code参数。 This is the exact same part from Expo documentation:这与 Expo 文档中的部分完全相同:

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

But unfortunately we could not find any method how we can use the parameter code to get the scope information, email, name, etc...但不幸的是,我们找不到任何方法可以使用参数code来获取范围信息、电子邮件、姓名等...

Can anybody guide us how we can use the object code to retrieve these data?谁能指导我们如何使用目标code来检索这些数据? (The Okta documentation is not clear for this either, so we are stuck.) (Okta 文档对此也不清楚,所以我们被卡住了。)

Edit 1:编辑 1:

The response has the following structure: response具有以下结构:

response: {
    "type": "success",
    "error": null,
    "url": "http://localhost:19006/?code=fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs&state=3XXXXXXXXz",
    "params": {
        "code": "fUMjE4kBX2QZXXXXXX_XXXXXXXMQ084kEPrTqDa9FTs",
        "state": "3XXXXXXXXz"
    },
    "authentication": null,
    "errorCode": null
}

Edit 2:编辑 2:

Calling exchangeCodeAsync also yields errors.调用exchangeCodeAsync也会产生错误。

Code:代码:

    const tokenRequestParams = {
        code: code,
        clientId: config.okta.clientId,
        redirectUri: oktaRedirectUri,
        extraParams: {
            code_verifier: authRequest.codeVerifier
        },
    }

    const tokenResult = await exchangeCodeAsync(tokenRequestParams, discovery);

Error:错误:

TokenRequest.ts:205 Uncaught (in promise) Error: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).  The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported.  If the client attempted to authenticate via the "Authorization" request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the "WWW-Authenticate" response header field matching the authentication scheme used by the client.
More info: Client authentication failed. Either the client or the client credentials are invalid.
    at AccessTokenRequest.<anonymous> (TokenRequest.ts:205:1)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3:1)
    at _next (asyncToGenerator.js:22:1)

PS: I asked the same question to the Expo forums also here . PS:我也在世博论坛问了同样的问题 If we can solve there, I plan to reflect to here also for wider audience.如果我们能在那里解决,我打算在这里也为更广泛的观众反映。 (The method can be related with Okta rather than Expo itself.) (该方法可能与 Okta 有关,而不是 Expo 本身。)

there are two ways to use Okta in React Native在 React Native 中有两种使用 Okta 的方法

1. By Restful APIs, using fetch/Axios 1. 通过 Restful API,使用 fetch/Axios

2. By Using Native SDK 2. 使用原生SDK

By Restful APIs, using fetch/Axios通过 Restful API,使用 fetch/Axios

here is the full code of okta using restful 这是使用 restful 的 okta 的完整代码

import React, { useState } from "react";

import {
  ScrollView,
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
  Platform,
} from "react-native";

import {
  useAutoDiscovery,
  useAuthRequest,
  makeRedirectUri,
  exchangeCodeAsync,
} from "expo-auth-session";
import { maybeCompleteAuthSession } from "expo-web-browser";
import axios from "axios";

const oktaConfig = {
  okta_issuer_url: "",
  okta_client_id: "",
  okta_callback_url: "com.okta.<OKTA_DOMAIN>:/callback",
};
export default App = (props) => {
  const useProxy = true;

  if (Platform.OS === "web") {
    maybeCompleteAuthSession();
  }

  const discovery = useAutoDiscovery(oktaConfig.okta_issuer_url);

  // When promptAsync is invoked we will get back an Auth Code
  // This code can be exchanged for an Access/ID token as well as
  // User Info by making calls to the respective endpoints

  const [authRequest, response, promptAsync] = useAuthRequest(
    {
      clientId: oktaConfig.okta_client_id,
      scopes: ["openid", "profile"],
      redirectUri: makeRedirectUri({
        native: oktaConfig.okta_callback_url,
        useProxy,
      }),
    },
    discovery
  );

  async function oktaCognitoLogin() {
    const loginResult = await promptAsync({ useProxy });
    ExchangeForToken(loginResult, authRequest, discovery);
  }

  return (
    <View style={styles.container}>
      <View style={styles.buttonContainer}>
        <TouchableOpacity
          style={styles.equalSizeButtons}
          onPress={() => oktaCognitoLogin()}
        >
          <Text style={styles.buttonText}>Okta Login</Text>
        </TouchableOpacity>
      </View>
      <ScrollView>
        {response && <Text>{JSON.stringify(response, null, 2)}</Text>}
      </ScrollView>
    </View>
  );
};

this is how, we can get exchange token and then get user info by using restful api就是这样,我们可以获取交换令牌,然后使用 restful api 获取用户信息


//After getting the Auth Code we need to exchange it for credentials
async function ExchangeForToken(response, authRequest, discovery) {
  // React hooks must be used within functions
  const useProxy = true;
  const expoRedirectURI = makeRedirectUri({
    native: oktaConfig.okta_callback_url,
    useProxy,
  })

  const tokenRequestParams = {
    code: response.params.code,
    clientId: oktaConfig.okta_client_id,
    redirectUri: expoRedirectURI,
    extraParams: {
      code_verifier: authRequest.codeVerifier
    },
  }
  
  const tokenResult = await exchangeCodeAsync(
      tokenRequestParams,
      discovery
  )

  const creds = ExchangeForUser(tokenResult)

  const finalAuthResult = {
    token_res : tokenResult,
    user_creds : creds
  }
  console.log("Final Result: ", finalAuthResult)
}

this is how we can get user info by using restful api这就是我们如何使用 restful api 获取用户信息

async function ExchangeForUser(tokenResult) {
  const accessToken = tokenResult.accessToken;
  const idToken = tokenResult.idToken;

  //make an HTTP direct call to the Okta User Info endpoint of our domain
  const usersRequest = `${oktaConfig.okta_issuer_url}/v1/userinfo`
  const userPromise = await axios.get(usersRequest, {
    headers: {
      'Authorization': `Bearer ${accessToken}`
    }
  });
    
  console.log(userPromise, "user Info");
}



const styles = StyleSheet.create({
  container: {
    margin: 10,
    marginTop: 20,
  },
  buttonContainer: {
    flexDirection: "row",
    alignItems: "center",
    margin: 5,
  },
  equalSizeButtons: {
    width: "50%",
    backgroundColor: "#023788",
    borderColor: "#6df1d8",
    flexDirection: "row",
    justifyContent: "center",
    alignItems: "center",
    padding: 9,
    borderWidth: 1,
    shadowColor: "#6df1d8",
    shadowOpacity: 8,
    shadowRadius: 3,
    shadowOffset: {
      height: 0,
      width: 0,
    },
  },
  buttonText: {
    color: "#ffffff",
    fontSize: 16,
  },
});

Reference Code 参考代码

By Using Native SDK通过使用原生 SDK

for native SDK, you can use okta-react-native package like this对于本机 SDK,您可以像这样使用okta-react-native

Login Screen登录屏幕

import React from 'react';
import { 
  Alert,
  Button, 
  StyleSheet, 
  TextInput,
  View,  
  ActivityIndicator 
} from 'react-native';

import {
  signIn,
  introspectIdToken
} from '@okta/okta-react-native';

export default class CustomLogin extends React.Component {
  
  constructor(props) {
    super(props);
    this.state = { 
      isLoading: false,
      username: '',
      password: '',
    };  
  }
  
  async componentDidMount() {
    
  }

  signInCustom = () => {
    this.setState({ isLoading: true });
    signIn({ username: this.state.username, password: this.state.password })
      .then(() => {
        introspectIdToken()
          .then(idToken => {
            this.props.navigation.navigate('ProfilePage', { idToken: idToken, isBrowserScenario: false });
          }).finally(() => {
            this.setState({ 
              isLoading: false,
              username: '', 
              password: '',
            });
          });
      })
      .catch(error => {
        // For some reason the app crashes when only one button exist (only with loaded bundle, debug is OK) 🤦‍♂️
        Alert.alert(
          "Error",
          error.message,
          [
            {
              text: "Cancel",
              onPress: () => console.log("Cancel Pressed"),
              style: "cancel"
            },
            { text: "OK", onPress: () => console.log("OK Pressed") }
          ]
        );
    

        this.setState({
          isLoading: false
        });
      });
  }

  render() {
    if (this.state.isLoading) {
      return (
        <View style={styles.container}>
          <ActivityIndicator size="large" />
        </View>
      );
    }

    return (
      <View style={styles.container}>
        <TextInput 
          style={styles.input}
          placeholder='Username'
          onChangeText={input => this.setState({ username: input })}
          testID="username_input"
        />
        <TextInput 
          style={styles.input}
          placeholder='Password'
          onChangeText={input => this.setState({ password: input })}
          testID="password_input"
        />
        <Button 
          onPress={this.signInCustom} 
          title="Sign in" 
          testID='sign_in_button' 
        />
        <View style={styles.flexible}></View>
      </View>  
    ); 
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  input: {
    height: 40,
    width: '80%',
    margin: 12,
    borderWidth: 1,
    padding: 10,
  },
  flexible: {
    flex: 1,
  }
});

Profile Screen配置文件屏幕

import React from 'react';
import { 
  Text,
  Button, 
  StyleSheet, 
  TextInput,
  View,
} from 'react-native';

import {
  signOut,
  revokeAccessToken,
  revokeIdToken,
  clearTokens,
} from '@okta/okta-react-native';

export default class ProfilePage extends React.Component {
  constructor(props) {
    super(props);

    this.state = { 
      idToken: props.route.params.idToken,
      isBrowserScenario: props.route.params.isBrowserScenario
    };
  }

  logout = () => {
    if (this.state.isBrowserScenario == true) {
      signOut().then(() => {
        this.props.navigation.popToTop();
      }).catch(error => {
        console.log(error);
      });
    }

    Promise.all([revokeAccessToken(), revokeIdToken(), clearTokens()])
      .then(() => {
        this.props.navigation.popToTop();
      }).catch(error => {
        console.log(error);
      });
  }

  render() {
    return (
      <View style={styles.container}>
        <Text testID="welcome_text">Welcome back, {this.state.idToken.preferred_username}!</Text>
        <Button 
          onPress={this.logout}
          title="Logout"
          testID="logout_button"
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Reference Code 参考代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 关于如何为 EXPO 反应本机应用程序设置单个 redirectURL 以与 MS Azure AD 身份验证一起使用的建议 - Advice on how to set single redirectURL for EXPO react native app for use with MS Azure AD Authentication 如何从this.props.navigation.navigate捕获参数? - How to catch the params from this.props.navigation.navigate? 如何在基于类的组件中获取 React Navigation 中的参数? - How to get params in React Navigation in class-based components? 您能否在具有苹果 2 因素身份验证的 CI 环境中使用“expo build:ios”以及如何使用 - Can you use "expo build:ios" on a CI environment with apple 2 factor authentication and how 在苹果身份验证期间获取 email id 值 null 作为响应 - Getting email id value null as response during apple-authentication 如何在将参数传递给组件之前从 AsyncStorage 获取参数? - How to get params from AsyncStorage before the passing them to a component? 如何在 expo 应用程序中使用 cookies 进行身份验证 - how to do authentication using cookies in expo app Axios PUT 带参数的数据 - Axios PUT Data with Params React Native,在选项卡导航器中按下选项卡时如何将参数传递到屏幕? - React Native, how do I pass params to a screen when pressing the tab in a tab navigator? 如何修复在 Windows 上使用 Expo 时网络响应超时? - How to fix Network Response Timed Out when Using Expo on Windows?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM