在React应用中实施Okta认证

1,008 阅读7分钟

对大多数应用程序来说,验证用户的身份是最重要的。建立你自己的认证系统一开始可能很简单,但当你考虑到安全桥梁和与第三方(如谷歌或Facebook)整合的便利性时,你可以看到建立和维护你自己的认证系统会很快变得乏味和复杂。

一个好的方法是使用一个专门处理认证的解决方案,如Okta。Okta为你提供了一个专门的安全专家团队,这样你就不必担心处理用户数据的麻烦了。

在本教程中,我们将告诉你如何在React中实现Okta认证。要跟着学习,你应该熟悉JavaScript和React Hooks

设置一个React应用程序

第一个要求是注册Okta的开发者版本。这样我们就可以进入管理面板,在那里我们将创建登录方法和我们的应用程序类型--在我们的例子中,是一个单页应用程序(SPA)。

Application Page To Create A Single-Page Application

开始与Okta集成的应用路径。

在应用路径下,我们可以生成一个应用集成(我们的登录方式)。

Applications Creation Page Highlighting Create App Integration Button

选择登录方式的应用集成按钮。

点击 "创建应用集成"按钮,会生成一个包含登录方法的对话框。由于我们的应用程序是一个SPA,而且我们没有连接到后台,我们将使用OpenID Connect登录方法,它为我们提供了一个登录小部件(我们以后也会建立我们自己的自定义登录表格)。

Pop-Up Confirming A OpenID Connect Sign-In Method

在选择应用集成后,我们要选择单页应用并继续。

下一步是指定一个签到重定向URI(一个回调URI)。这非常重要,因为当使用OpenID Connect登录方法时,我们会被重定向到Okta托管的登录小工具(与我们的应用程序不同的端点),并且需要一个回调URL,Okta会将用户的详细信息返回给我们的应用程序。

你也可以指定其他路由,比如签出重定向URI,当我们注销应用程序时,它是应用程序应该被重定向到的页面。对于我们的用例,我们可以指定基本URL/登录路线作为我们的注销端点,这意味着用户会被重定向到登陆页面或登录页面,这取决于我们指定的端点。如果你不指定端点,用户就会被重定向到Okta的登录小工具上

Base URI是可选的。如果你打算自我托管Okta签到小工具,这很有用,但不在本教程的范围内。

与React集成

现在我们已经准备好了与Okta的应用集成,让我们把它与我们的React应用集成。

在处理这个集成时,要特别注意客户ID和Okta域,这两个都可以在应用标签中找到。

第一步是安装Okta SDK和Okta Auth JavaScript SDK。

# yarn
yarn add @okta/okta-auth-js @okta/okta-react

# npm
npm install --save @okta/okta-auth-js @okta/okta-react

让我们为我们的Okta配置数据创建环境变量(以.env 文件的形式)。

OKTA_DOMAIN=<YOUR_OKTA_DOMAIN>
CLIENT_ID=<YOUR_CLIENT_ID>
CALLBACK_PATH='/login/callback'
ISSUER='https://<YOUR_OKTA_DOMAIN>/oauth2/default'
HOST='window.location.host'
SCOPES='openid profile email'

现在我们可以配置我们的应用程序来使用Okta。

import React from "react";
import { BrowserRouter as Router, Route, useHistory } from "react-router-dom";
import { Security, SecureRoute } from "@okta/okta-react";
import { OktaAuth, toRelativeUrl } from "@okta/okta-auth-js";
import { LandingPage } from "./LandingPage";
import { Dashboard } from "./Dashboard";
import { Header } from "./Header";
const CLIENT_ID = process.env.CLIENT_ID;
const CALLBACK_PATH = process.env.CALLBACK_PATH;
const ISSUER = process.env.ISSUER;
const HOST = process.env.HOST;
const REDIRECT_URI = `http://${HOST}${CALLBACK_PATH}`;
const SCOPES = process.env.SCOPES;

if (!SCOPES || !CLIENT_ID || !CALLBACK_PATH || !ISSUER || !HOST) {
  throw new Error("All environmental variables must be set");
}

const config = {
  issuer: ISSUER,
  clientId: CLIENT_ID,
  redirectUri: REDIRECT_URI,
  scopes: SCOPES.split(/\s+/),
};

const oktaAuth = new OktaAuth(config);

const App = () => {
  const history = useHistory();
  const restoreOriginalUri = async (_oktaAuth: any, originalUri: any) => {
    history.replace(toRelativeUrl(originalUri || "/", window.location.origin));
  };
  return (
    <Router>
      <Security restoreOriginalUri={restoreOriginalUri} oktaAuth={oktaAuth}>
        <Header />
        <Route path="/" exact={true} component={LandingPage} />
        <SecureRoute path="/dashboard" exact={true} component={Dashboard} />
      </Security>
    </Router>
  );
};

export default App;

一个新的oktaAuth 实例是用配置对象创建的。这将被传递到安全组件中,该组件封装了我们应用程序的所有路由,并为其所有的子代(路由中的组件)提供oktaAuthauthState 对象。

oktaAuth 对象可以用来改变或读取关于认证状态的信息。

authState 对象包含。

  • isAuthenticated ,一个布尔值,表示用户是否被认证。如果idTokenaccessToken 中存在,该值为真。tokenManager
  • accessToken, 一个分配给当前认证用户的JWT访问令牌
  • idToken, 一个分配给当前认证用户的JWT ID令牌
  • error ,如果认证过程失败,则返回该标志。

还请注意,我们不需要处理SecureRoute ourselves ;Okta提供了一个开箱即用的路由,它可以保护指定的路径。

让我们实现一个登录按钮来触发签到小部件。这在标题组件中很常见,所以我们将在那里处理它。

我们被提供了一个useOktaAuth 钩子,它让我们可以访问oktaAuthauthState 对象。

import React from "react";
import { useOktaAuth } from "@okta/okta-react";

function Header() {
  const { authState, oktaAuth } = useOktaAuth();
  const loginWithRedirect = () =>
    oktaAuth.signInWithRedirect({ originalUri: "/dashboard" });
  const logOut = () => oktaAuth.signOut();

  const buttonText = authState.isAuthenticated ? "Logout" : "Login";
  const btnLogic = authState.isAuthenticated ? logOut : loginWithRedirect;

  return (
    <>
      <div>Okta React</div>
      <button onClick={btnLogic}>{buttonText}</button>
    </>
  );
}

export { Header };

我们使用authState 来确定我们是否要登录或退出我们的应用程序。

loginWithRedirect 函数处理登录触发器,在成功登录时重定向到仪表板路由,如下图所示。

Okta's Login Page For Users

登录后,用户会立即被重定向到仪表盘路由。

为了使注销功能在localhost中工作,我们需要将localhost添加到我们的信任路由列表中。这将允许在我们的应用程序中跨源。

Page To Add Trusted Routes

现在,如果我们点击标题中的注销按钮,它会成功地将我们的用户注销,并将他们重定向到登陆页面。

我们成功了!我们已经在React应用程序中成功实现了Okta认证。

自定义签到表单

Okta的登录界面很好,也很容易上手,但假设我们想实现一个设计独特的自定义登录表单,同时让我们的用户留在页面上而不重定向到Okta。

我们可以通过调用useOktaAuth 钩子时提供的oktaAuth 来实现这一点。

让我们来实现一个自定义的签到表单。

import React from "react";
import { useOktaAuth } from "@okta/okta-react/bundles/types";

function SignIn() {
  const { oktaAuth } = useOktaAuth();
  const [sessionToken, setSessionToken] = React.useState<string | null>(null);

  const onSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
    event.preventDefault();

    const username = event.currentTarget.elements.namedItem(
      "username"
    ) as HTMLInputElement;
    const password = event.currentTarget.elements.namedItem(
      "password"
    ) as HTMLInputElement;

    const data = {
      username: username.value,
      password: password.value,
    };

    oktaAuth
      .signInWithCredentials(data)
      .then((res) => {
        const sessionToken = res.sessionToken;
        if (!sessionToken) {
          throw new Error("authentication process failed");
        }
        setSessionToken(sessionToken);
        oktaAuth.signInWithRedirect({
          originalUri: "/dashboard",
          // @ts-ignore
          sessionToken: sessionToken,
        });
      })
      .catch((err) => console.log("handle error here": err));
  };

  if (sessionToken) return <div />;

  return (
    <div>
      <form onSubmit={onSubmit}>
        <label htmlFor="username">Username</label>
        <input type="text" id="username" />
        <label htmlFor="password">Password</label>
        <input type="password" id="password" />
        <button type="submit">SignIn</button>
      </form>
    </div>
  );
}
export { SignIn };

我们有自己的表单,我们利用oktaAuth 中的signInWithCredentials 选项来处理一个自定义的表单提交。sessionToken ,是一个一次性使用的令牌,是成功认证后收到的值。当调用useOktaAuth 钩子时,我们仍然可以通过authState 对象中提供的ID或访问令牌获得详细信息。

登录成功后,我们可以使用signInWithRedirect 函数将用户发送到原始URI(主要是受保护的仪表盘路由),同时包括sessionToken

让我们运行我们的应用程序,将登录按钮连接到这个登录表单的端点,同时更新我们的路由。

import React from "react";
import { useOktaAuth } from "@okta/okta-react";
import { Link } from "react-router-dom";

function Header() {
  const { authState, oktaAuth } = useOktaAuth();

  if (!authState) return null;

  const logOutRedirect = async () => await oktaAuth.signOut();

  function btnToRender() {
    return authState.isAuthenticated ? (
      <button onClick={logOutRedirect}>LogOut</button>
    ) : (
      <button>
        <Link to="/login">Login</Link>
      </button>
    );
  }

  return (
    <>
      <div>Okta React</div>
      {btnToRender()}
    </>
  );
}
export { Header };

路线的更新如下所示。

<Security restoreOriginalUri={restoreOriginalUri} oktaAuth={oktaAuth}>
  <Header />
  <Route path="/" exact={true} component={LandingPage} />
  <Route path="/login" component={SignIn} />
  <SecureRoute path="/dashboard" exact={true} component={Dashboard} />
  <Route path={CALLBACK_PATH} component={LoginCallback} />
</Security>

现在,当我们点击我们的登录按钮时,我们被重定向到登录页面。

祝贺你!你已经成功地实现了一个自定义的登录。你已经成功地在你的React应用中用Okta实现了一个自定义的登录界面。

结论

用户数据是非常微妙的信息,如果被泄露,会导致严重的隐私问题。在本教程中,我们向你展示了如何在React应用中用Okta实现一个更安全的方式来处理认证。

对于不构建后端服务的前端开发者来说--考虑到JAMStack和serverless等框架的兴起,使你能够在没有实际API服务器的情况下构建应用程序--Okta提供了一种在React应用程序中实现身份验证的好方法,而没有过多的复杂性。

The postImplementing Okta authentication in a React appappeared first onLogRocket Blog.