如何使用 nodejs 和 okta 进行应用程序注销

Posted

技术标签:

【中文标题】如何使用 nodejs 和 okta 进行应用程序注销【英文标题】:How to do an app logout using nodejs and okta 【发布时间】:2021-11-10 02:30:47 【问题描述】:

我目前正在使用带有 OIDC 和 Node Express 的 Okta 来登录我制作的 Web 应用程序。我希望我的用户能够仅注销我的应用程序而不是 okta 本身,因为 okta 是为我的公司服务的,这将使他们从使用公司 okta 的所有其他网站中注销。 Okta 建议在他们的网站上这样做。

app.get('/local-logout', (req, res) => 
  req.logout();
  res.redirect('/');
);

(https://developer.okta.com/docs/guides/sign-users-out/nodeexpress/sign-out-of-your-app/)

我已经尝试过实现它,但它不起作用。我也尝试过使用 req.session.destroy、res.clearCookie("connect.sid")、req.session = null 以及这些有或没有回调的许多不同组合。每当我重定向时,它只会返回主页并且同一用户已登录。每当我尝试删除 connect.sid cookie 时,它​​都会删除,然后在用户被重定向时重新初始化。我不确定该怎么做。我删除了不相关端点的代码如下。


require('dotenv').config();

import 'zone.js/dist/zone-node';

import  ngExpressEngine  from '@nguniversal/express-engine';
import  join  from 'path';
import * as express from 'express';
import  AppServerModule  from './src/main.server';
import  APP_BASE_HREF  from '@angular/common';
import  existsSync, read  from 'fs';


// 30 minutes
const sessionMaxAge = 1800000;

import * as e from 'cors';
import  resolveForwardRef  from '@angular/core';
import  User  from '@okta/okta-sdk-nodejs';

const session = require('express-session');
const  ExpressOIDC  = require('@okta/oidc-middleware');
const cookieParser = require("cookie-parser");


 /** Creates the OpenID Connect Middleware and configures it to work with Okta */
 const oidc = new ExpressOIDC(
  appBaseUrl: process.env.HOST_URL,
  issuer: `$process.env.OKTA_ORG_URL/oauth2/default`,
  client_id: process.env.OKTA_CLIENT_ID,
  client_secret: process.env.OKTA_CLIENT_SECRET,
  redirect_uri: process.env.REDIRECT_URL,
  scope: 'openid profile',
  routes: 
    loginCallback: 
      path: '/authorization-code/callback'
    ,
  
);


// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express 
  var root_folder: string = process.env.ROOT_FOLDER || "dist/angular-app/browser"
  const server = express();
  const distFolder = join(process.cwd(), root_folder);
  const indexhtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  // Our Universal express-engine (found @ https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_angular_universal_tree_master_modules_express-2Dengine&d=DwIGAg&c=-35OiAkTchMrZOngvJPOeA&r=03NdPO1x-l0QAZ_R9TNGwA&m=WF5ia-YADjCituVWMV5vLoZ5wg7d_W1qhCYDTbJNGT0&s=WPOkeRsetPDQ6TrD26RKLo1m9_zxBfQXGhUUSkth0Ew&e= )
  server.engine('html', ngExpressEngine(
    bootstrap: AppServerModule,
  ));

  server.set('view engine', 'html');
  server.set('views', distFolder); 

  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, 
    maxAge: '1y'
  ));

  // Configure Session Support
  server.use( session(
      secret: process.env.APP_SECRET,
      resave: false,
      saveUninitialized: false,
      cookie: maxAge: sessionMaxAge
    )
  );
  
  server.use(cookieParser());
  server.use(oidc.router);

   // Log the user out of the local session
   server.get('/local-logout',(req:any, res:any) => 
    req.logout();
    res.redirect('/');
  );

  // All regular routes use the Universal engine
  server.get('*', oidc.ensureAuthenticated( redirectTo: '/login' ), (req: any, res: any) => 
    if(req.session && req.session.username != null) 
      console.log(process.env);
      res.render(indexHtml,  req, providers: [ provide: APP_BASE_HREF, useValue: req.baseUrl ] );
    
    else 
      console.log("getting user info");
      getUserInfo(req)
      .then(userInfo => 
        req.session.username = userInfo.username;
        req.session.group = userInfo.group;
        if (userInfo.group=="unauthorized") 
          res.sendFile('./403.html', root: ".")
        
        else
          console.log(process.env);
          res.render(indexHtml,  req, providers: [ provide: APP_BASE_HREF, useValue: req.baseUrl ] );
        
      );
  
  );

  return server;

  function run(): void 
    const port = process.env.PORT || 4000;

    // Start up the Node server
    const server = app();
    server.listen(port, () => 
      console.log(`Node Express server listening on https://urldefense.proofpoint.com/v2/url?u=http-3A__localhost-3A-24-257Bport-257D&d=DwIGAg&c=-35OiAkTchMrZOngvJPOeA&r=03NdPO1x-l0QAZ_R9TNGwA&m=WF5ia-YADjCituVWMV5vLoZ5wg7d_W1qhCYDTbJNGT0&s=PUb7XMS4uP9ICqUY28QgXNRxoWk6sGatPdmZMqmgbJs&e= `);
    );


// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) 
  run();


export * from './src/main.server';

【问题讨论】:

【参考方案1】:

经过详尽的搜索,我认为这是不可能的。我最终做了req.logout(),然后重定向到一个页面,上面写着“您已退出应用程序,但仍可能登录到您的单点登录提供商。”

【讨论】:

以上是关于如何使用 nodejs 和 okta 进行应用程序注销的主要内容,如果未能解决你的问题,请参考以下文章

okta 对应用程序进行身份验证时如何获取会话

如何配置 Spring Security SAML 以与 Okta 一起使用?

在 JAVA 中通过身份验证后如何获取 okta 用户详细信息/当前会话

如何使用我的数据库创建身份验证服务器 OKTA?

Okta登录小部件获取名称和头像

带有 okta OAUTH 令牌身份验证的 Django Rest API