mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 04:40:45 +08:00
Query user profile via intranet direct HTTP; upgrade/clear trust sessions without department.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
74a86ff832
commit
9ff9635624
5 changed files with 262 additions and 148 deletions
|
|
@ -4,7 +4,7 @@
|
|||
# UAC 基础 URL (生产: https://uac.zte.com.cn, 测试: http://uactest.zte.com.cn:8080)
|
||||
uacBaseUrl: https://uac.zte.com.cn
|
||||
|
||||
# icenterapi 用户搜索接口 (完整 URL)
|
||||
# icenterapi 用户搜索接口 (完整 URL;须内网直连,禁止走 HTTP(S)_PROXY)
|
||||
userSearchUrl: https://icenterapi.zte.com.cn/zte-km-icenter-addresearch/user/plain/docs/search
|
||||
|
||||
# 登录时传给 UAC 的业务系统 Code
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ async function handleVerifyCode(req, res) {
|
|||
}))
|
||||
}
|
||||
|
||||
// User info proxy - bypasses CORS by server-side fetching icenterapi
|
||||
// User info proxy — intranet direct (no HTTP_PROXY), empNo+token headers
|
||||
async function handleUserInfo(req, res) {
|
||||
if (req.method !== 'GET') {
|
||||
res.writeHead(405, { 'Content-Type': 'application/json' })
|
||||
|
|
@ -367,53 +367,35 @@ async function handleUserInfo(req, res) {
|
|||
const empNo = url.searchParams.get('empNo')
|
||||
const token = url.searchParams.get('token')
|
||||
|
||||
if (!empNo) {
|
||||
if (!empNo || !token) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json' })
|
||||
res.end(JSON.stringify({ error: 'Missing empNo' }))
|
||||
res.end(JSON.stringify({ error: 'Missing empNo or token' }))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const https = await import('node:https')
|
||||
const targetUrl = new URL(_currentConfig.userSearchUrl)
|
||||
const body = JSON.stringify({ employeeShortId: empNo, enableLabel: true, keyword: empNo })
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'Content-Length': Buffer.byteLength(body),
|
||||
[INTERNAL.empNoHeader]: empNo,
|
||||
'Origin': _currentConfig.uacBaseUrl,
|
||||
'Referer': _currentConfig.uacBaseUrl + '/'
|
||||
}
|
||||
if (!token) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json' })
|
||||
res.end(JSON.stringify({ error: 'Missing token' }))
|
||||
const { searchUserByEmpNoToken } = await import('./uds/user-search.js')
|
||||
const out = await searchUserByEmpNoToken({
|
||||
userSearchUrl: _currentConfig.userSearchUrl,
|
||||
empNo,
|
||||
token,
|
||||
empNoHeader: INTERNAL.empNoHeader,
|
||||
authValueHeader: INTERNAL.authValueHeader,
|
||||
origin: _currentConfig.uacBaseUrl,
|
||||
})
|
||||
if (!out.ok) {
|
||||
const status = out.statusCode === 401 || out.statusCode === 403 ? out.statusCode : 502
|
||||
res.writeHead(status, { 'Content-Type': 'application/json' })
|
||||
res.end(JSON.stringify({
|
||||
error: 'user search failed',
|
||||
reason: out.reason,
|
||||
hint: out.hint || 'Ensure userSearchUrl is intranet-reachable and Host does not force HTTP(S)_PROXY for *.zte.com.cn',
|
||||
detail: { code: out.code, msg: out.msg, statusCode: out.statusCode },
|
||||
}))
|
||||
return
|
||||
}
|
||||
headers[INTERNAL.authValueHeader] = token
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
const proxyReq = https.request({
|
||||
hostname: targetUrl.hostname,
|
||||
port: targetUrl.port || 443,
|
||||
path: targetUrl.pathname + targetUrl.search,
|
||||
method: 'POST',
|
||||
headers,
|
||||
timeout: 8000,
|
||||
}, (proxyRes) => {
|
||||
let data = ''
|
||||
proxyRes.on('data', chunk => data += chunk)
|
||||
proxyRes.on('end', () => {
|
||||
try { resolve(JSON.parse(data)) } catch (e) { resolve({ raw: data }) }
|
||||
})
|
||||
})
|
||||
proxyReq.on('error', reject)
|
||||
proxyReq.write(body)
|
||||
proxyReq.end()
|
||||
})
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' })
|
||||
res.end(JSON.stringify(result))
|
||||
res.end(JSON.stringify(out.result))
|
||||
} catch (err) {
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' })
|
||||
res.end(JSON.stringify({ error: err.message }))
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import { UdsClient } from '../uds/client.js'
|
||||
import { UdsValidator } from '../uds/validator.js'
|
||||
import { ROLES, computePermissions } from '../roles.js'
|
||||
import { searchUserByEmpNoToken } from '../uds/user-search.js'
|
||||
|
||||
/**
|
||||
* auth-middleware
|
||||
*
|
||||
* 正常登录:Cookie 中必须同时有 empNo + token,并用 userSearchUrl
|
||||
* (带 X-Emp-No / X-Auth-Value)拉用户详情;两者都成功才建会话。
|
||||
* (带 X-Emp-No / X-Auth-Value)直连内网拉用户详情;成功才建会话。
|
||||
* 出站请求绕过 HTTP(S)_PROXY。
|
||||
*
|
||||
* 兜底登录:仅认可已由 /api/fallback/login 写好的 administrator 会话;
|
||||
* 禁止仅靠伪造 UDS_FALLBACK_USER Cookie 提权。
|
||||
* 兜底登录:仅认可已由 /api/fallback/login 写好的 administrator 会话。
|
||||
*/
|
||||
export function createAuthMiddleware(config, sessionStore, rolesStore) {
|
||||
const udsClient = new UdsClient(config.udsAuth)
|
||||
|
|
@ -30,99 +31,46 @@ export function createAuthMiddleware(config, sessionStore, rolesStore) {
|
|||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* empNo + token → 调用户搜索接口;返回解析后的 profile 或 null
|
||||
*/
|
||||
function applyProfile(userContext, profile, credentials) {
|
||||
userContext.userId = profile.empNo
|
||||
userContext.empNo = profile.empNo
|
||||
userContext.username = profile.username
|
||||
userContext.displayName = profile.username
|
||||
userContext.department = profile.department
|
||||
userContext.organization = profile.organization || profile.department || ''
|
||||
userContext.email = profile.email
|
||||
userContext.phone = profile.phone
|
||||
userContext.token = credentials.token
|
||||
userContext.lang = credentials.lang || userContext.lang || 'zh-CN'
|
||||
userContext.isAuthenticated = true
|
||||
userContext.authMode = 'token+profile'
|
||||
userContext.lastActiveAt = new Date().toISOString()
|
||||
return userContext
|
||||
}
|
||||
|
||||
async function verifyEmpNoAndToken(empNo, token) {
|
||||
if (!userSearchUrl || !empNo || !token) {
|
||||
console.warn('[uds-auth] verifyEmpNoAndToken skipped: missing', {
|
||||
hasUrl: !!userSearchUrl, hasEmpNo: !!empNo, hasToken: !!token,
|
||||
})
|
||||
const out = await searchUserByEmpNoToken({
|
||||
userSearchUrl,
|
||||
empNo,
|
||||
token,
|
||||
empNoHeader: config.udsAuth?.empNoHeader || 'X-Emp-No',
|
||||
authValueHeader: config.udsAuth?.authValueHeader || 'X-Auth-Value',
|
||||
origin: uacBaseUrl || undefined,
|
||||
})
|
||||
if (!out.ok) {
|
||||
console.warn('[uds-auth] verifyEmpNoAndToken failed:', out)
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const targetUrl = new URL(userSearchUrl)
|
||||
const body = JSON.stringify({
|
||||
employeeShortId: empNo,
|
||||
enableLabel: true,
|
||||
keyword: empNo,
|
||||
})
|
||||
const headers = {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'Content-Length': Buffer.byteLength(body),
|
||||
[config.udsAuth?.empNoHeader || 'X-Emp-No']: empNo,
|
||||
[config.udsAuth?.authValueHeader || 'X-Auth-Value']: token,
|
||||
}
|
||||
if (uacBaseUrl) {
|
||||
headers.Origin = uacBaseUrl
|
||||
headers.Referer = uacBaseUrl.replace(/\/?$/, '/')
|
||||
}
|
||||
return out.profile
|
||||
}
|
||||
|
||||
const isHttps = targetUrl.protocol === 'https:'
|
||||
const mod = await import(isHttps ? 'node:https' : 'node:http')
|
||||
const { statusCode, result } = await new Promise((resolve, reject) => {
|
||||
const req = mod.request({
|
||||
hostname: targetUrl.hostname,
|
||||
port: targetUrl.port || (isHttps ? 443 : 80),
|
||||
path: targetUrl.pathname + targetUrl.search,
|
||||
method: 'POST',
|
||||
headers,
|
||||
timeout: 8000,
|
||||
}, (res) => {
|
||||
let data = ''
|
||||
res.on('data', (c) => { data += c })
|
||||
res.on('end', () => {
|
||||
let parsed = null
|
||||
try { parsed = JSON.parse(data) } catch { parsed = { raw: String(data).slice(0, 300) } }
|
||||
resolve({ statusCode: res.statusCode, result: parsed })
|
||||
})
|
||||
})
|
||||
req.on('error', reject)
|
||||
req.on('timeout', () => { req.destroy(); reject(new Error('user-info timeout')) })
|
||||
req.write(body)
|
||||
req.end()
|
||||
})
|
||||
|
||||
const code = result?.code?.code ?? result?.code
|
||||
if (code !== '0000' && code !== 0 && code !== '0') {
|
||||
console.warn('[uds-auth] userSearch failed:', {
|
||||
empNo, statusCode, code, msg: result?.code?.msg || result?.msg || result?.raw,
|
||||
})
|
||||
return null
|
||||
}
|
||||
const list = Array.isArray(result?.bo) ? result.bo
|
||||
: Array.isArray(result?.bo?.rows) ? result.bo.rows
|
||||
: Array.isArray(result?.bo?.list) ? result.bo.list
|
||||
: []
|
||||
if (!list.length) {
|
||||
console.warn('[uds-auth] userSearch empty bo:', { empNo, statusCode, keys: result && Object.keys(result) })
|
||||
return null
|
||||
}
|
||||
const emp = list[0]
|
||||
const resolvedEmpNo = String(
|
||||
emp.employeeShortId || emp.employeeNO || emp.empUIID || emp.empNo || empNo,
|
||||
).trim()
|
||||
// 工号必须对得上(防 token 有效但查了别人)
|
||||
if (resolvedEmpNo && resolvedEmpNo !== String(empNo).trim()
|
||||
&& !String(empNo).includes(resolvedEmpNo)
|
||||
&& !resolvedEmpNo.includes(String(empNo).trim())) {
|
||||
// 宽松:短工号/长工号互含即通过;完全无关则拒绝
|
||||
const a = String(empNo).replace(/\D/g, '')
|
||||
const b = resolvedEmpNo.replace(/\D/g, '')
|
||||
if (!a || !b || !(a.includes(b) || b.includes(a))) return null
|
||||
}
|
||||
return {
|
||||
empNo: String(empNo).trim(),
|
||||
username: emp.name || emp.empName || emp.userName || empNo,
|
||||
department: emp.deptName || emp.deptShortName || emp.orgName || emp.department || '',
|
||||
email: emp.email || emp.mail || '',
|
||||
phone: emp.mobile || emp.phone || '',
|
||||
token,
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[uds-auth] verifyEmpNoAndToken failed:', err.message)
|
||||
return null
|
||||
}
|
||||
function needsProfileUpgrade(userContext) {
|
||||
if (!userContext) return true
|
||||
if (userContext.authMode === 'trust') return true
|
||||
if (userContext.authMode !== 'token+profile') return true
|
||||
const name = userContext.displayName || userContext.username || ''
|
||||
if (!name || name === userContext.empNo) return true
|
||||
return false
|
||||
}
|
||||
|
||||
async function authMiddleware(ctx, next) {
|
||||
|
|
@ -132,8 +80,31 @@ export function createAuthMiddleware(config, sessionStore, rolesStore) {
|
|||
|
||||
if (!extracted) return next()
|
||||
|
||||
// 1. 已有会话 → 滑动续期
|
||||
let userContext = await sessionStore.get(extracted.empNo)
|
||||
|
||||
// 旧 trust 会话 / 无姓名部门:强制用 token 重查用户信息
|
||||
if (userContext && extracted.kind === 'uds' && needsProfileUpgrade(userContext)) {
|
||||
const credentials = udsValidator.extractCredentials(req)
|
||||
if (credentials && credentials.empNo === extracted.empNo && udsValidator.validateCredentials(credentials)) {
|
||||
const profile = await verifyEmpNoAndToken(credentials.empNo, credentials.token)
|
||||
if (profile) {
|
||||
userContext = applyProfile(userContext, profile, credentials)
|
||||
await sessionStore.setex(
|
||||
profile.empNo,
|
||||
Math.floor(cookieMaxAge / 1000),
|
||||
userContext,
|
||||
)
|
||||
} else {
|
||||
// 查不到资料则作废 trust 会话,避免“假登录”
|
||||
await sessionStore.delete(extracted.empNo)
|
||||
userContext = null
|
||||
}
|
||||
} else if (userContext.authMode === 'trust') {
|
||||
await sessionStore.delete(extracted.empNo)
|
||||
userContext = null
|
||||
}
|
||||
}
|
||||
|
||||
if (userContext) {
|
||||
if (slidingExpiration) {
|
||||
userContext.lastActiveAt = new Date().toISOString()
|
||||
|
|
@ -151,13 +122,10 @@ export function createAuthMiddleware(config, sessionStore, rolesStore) {
|
|||
return next()
|
||||
}
|
||||
|
||||
// 2. 无会话
|
||||
// 兜底 Cookie:绝不在这里建会话(必须走密码登录)
|
||||
if (extracted.kind === 'fallback') {
|
||||
return next()
|
||||
}
|
||||
|
||||
// UDS:必须 empNo + token,且用户详情接口校验通过
|
||||
const credentials = udsValidator.extractCredentials(req)
|
||||
if (!credentials || !udsValidator.validateCredentials(credentials)) {
|
||||
return next()
|
||||
|
|
@ -176,19 +144,15 @@ export function createAuthMiddleware(config, sessionStore, rolesStore) {
|
|||
token: credentials.token,
|
||||
lang: credentials.lang,
|
||||
username: profile.username,
|
||||
displayName: profile.username,
|
||||
department: profile.department,
|
||||
organization: profile.organization,
|
||||
email: profile.email,
|
||||
phone: profile.phone,
|
||||
}, false)
|
||||
userContext.username = profile.username
|
||||
userContext.displayName = profile.username
|
||||
userContext.department = profile.department
|
||||
userContext.email = profile.email
|
||||
userContext.phone = profile.phone
|
||||
applyProfile(userContext, profile, credentials)
|
||||
userContext.authenticatedAt = new Date().toISOString()
|
||||
userContext.lastActiveAt = new Date().toISOString()
|
||||
userContext.isAuthenticated = true
|
||||
userContext.authMode = 'token+profile'
|
||||
userContext.sessionCreatedAt = new Date().toISOString()
|
||||
|
||||
await sessionStore.setex(
|
||||
profile.empNo,
|
||||
|
|
|
|||
163
uds-auth/lib/uds/user-search.js
Normal file
163
uds-auth/lib/uds/user-search.js
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
* Outbound HTTP(S) that must NOT use HTTP_PROXY / HTTPS_PROXY.
|
||||
* ZTE icenter / UAC user APIs are intranet-only; corporate forward proxies
|
||||
* typically return 401/403 and never reach the real service.
|
||||
*/
|
||||
import http from 'node:http'
|
||||
import https from 'node:https'
|
||||
|
||||
/**
|
||||
* @param {string|URL} url
|
||||
* @param {{ method?: string, headers?: Record<string,string|number>, body?: string|Buffer, timeoutMs?: number }} opts
|
||||
* @returns {Promise<{ statusCode: number, headers: object, body: string, json: any }>}
|
||||
*/
|
||||
export function directRequest(url, opts = {}) {
|
||||
const target = typeof url === 'string' ? new URL(url) : url
|
||||
const isHttps = target.protocol === 'https:'
|
||||
const lib = isHttps ? https : http
|
||||
const method = (opts.method || 'GET').toUpperCase()
|
||||
const body = opts.body == null ? null : Buffer.isBuffer(opts.body) ? opts.body : Buffer.from(String(opts.body))
|
||||
const headers = { ...(opts.headers || {}) }
|
||||
if (body && headers['Content-Length'] == null && headers['content-length'] == null) {
|
||||
headers['Content-Length'] = body.length
|
||||
}
|
||||
const timeoutMs = opts.timeoutMs ?? 8000
|
||||
|
||||
// Fresh Agent — never inherit globalAgent (often patched by proxy bootstraps).
|
||||
const agent = new lib.Agent({ keepAlive: false })
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = lib.request({
|
||||
protocol: target.protocol,
|
||||
hostname: target.hostname,
|
||||
port: target.port || (isHttps ? 443 : 80),
|
||||
path: target.pathname + target.search,
|
||||
method,
|
||||
headers,
|
||||
agent,
|
||||
timeout: timeoutMs,
|
||||
}, (res) => {
|
||||
const chunks = []
|
||||
res.on('data', (c) => chunks.push(c))
|
||||
res.on('end', () => {
|
||||
const text = Buffer.concat(chunks).toString('utf8')
|
||||
let json = null
|
||||
try { json = JSON.parse(text) } catch { json = null }
|
||||
resolve({
|
||||
statusCode: res.statusCode || 0,
|
||||
headers: res.headers,
|
||||
body: text,
|
||||
json,
|
||||
})
|
||||
})
|
||||
})
|
||||
req.on('error', reject)
|
||||
req.on('timeout', () => {
|
||||
req.destroy()
|
||||
reject(new Error(`directRequest timeout after ${timeoutMs}ms: ${target.host}`))
|
||||
})
|
||||
if (body) req.write(body)
|
||||
req.end()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST userSearchUrl with X-Emp-No + X-Auth-Value (intranet, no proxy).
|
||||
*/
|
||||
export async function searchUserByEmpNoToken({
|
||||
userSearchUrl,
|
||||
empNo,
|
||||
token,
|
||||
empNoHeader = 'X-Emp-No',
|
||||
authValueHeader = 'X-Auth-Value',
|
||||
origin,
|
||||
timeoutMs = 8000,
|
||||
}) {
|
||||
if (!userSearchUrl || !empNo || !token) {
|
||||
return { ok: false, reason: 'missing_args' }
|
||||
}
|
||||
const body = JSON.stringify({
|
||||
employeeShortId: empNo,
|
||||
enableLabel: true,
|
||||
keyword: empNo,
|
||||
})
|
||||
const headers = {
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
[empNoHeader]: empNo,
|
||||
[authValueHeader]: token,
|
||||
}
|
||||
if (origin) {
|
||||
headers.Origin = origin
|
||||
headers.Referer = String(origin).replace(/\/?$/, '/')
|
||||
}
|
||||
|
||||
let res
|
||||
try {
|
||||
res = await directRequest(userSearchUrl, { method: 'POST', headers, body, timeoutMs })
|
||||
} catch (err) {
|
||||
return { ok: false, reason: 'network', error: err.message }
|
||||
}
|
||||
|
||||
if (res.statusCode === 401 || res.statusCode === 403) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: 'http_auth',
|
||||
statusCode: res.statusCode,
|
||||
msg: res.json?.code?.msg || res.json?.msg || res.body.slice(0, 200),
|
||||
hint: 'userSearchUrl must be reachable on intranet WITHOUT HTTP(S)_PROXY',
|
||||
}
|
||||
}
|
||||
|
||||
const result = res.json
|
||||
if (!result) {
|
||||
return { ok: false, reason: 'bad_json', statusCode: res.statusCode, raw: res.body.slice(0, 300) }
|
||||
}
|
||||
|
||||
const code = result?.code?.code ?? result?.code
|
||||
if (code !== '0000' && code !== 0 && code !== '0') {
|
||||
return {
|
||||
ok: false,
|
||||
reason: 'biz_code',
|
||||
statusCode: res.statusCode,
|
||||
code,
|
||||
msg: result?.code?.msg || result?.msg,
|
||||
}
|
||||
}
|
||||
|
||||
const list = Array.isArray(result?.bo) ? result.bo
|
||||
: Array.isArray(result?.bo?.rows) ? result.bo.rows
|
||||
: Array.isArray(result?.bo?.list) ? result.bo.list
|
||||
: []
|
||||
if (!list.length) {
|
||||
return { ok: false, reason: 'empty', statusCode: res.statusCode, result }
|
||||
}
|
||||
|
||||
const emp = list[0]
|
||||
const resolvedEmpNo = String(
|
||||
emp.employeeShortId || emp.employeeNO || emp.empUIID || emp.empNo || empNo,
|
||||
).trim()
|
||||
if (resolvedEmpNo && resolvedEmpNo !== String(empNo).trim()
|
||||
&& !String(empNo).includes(resolvedEmpNo)
|
||||
&& !resolvedEmpNo.includes(String(empNo).trim())) {
|
||||
const a = String(empNo).replace(/\D/g, '')
|
||||
const b = resolvedEmpNo.replace(/\D/g, '')
|
||||
if (!a || !b || !(a.includes(b) || b.includes(a))) {
|
||||
return { ok: false, reason: 'emp_mismatch', resolvedEmpNo }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
profile: {
|
||||
empNo: String(empNo).trim(),
|
||||
username: emp.name || emp.empName || emp.userName || empNo,
|
||||
department: emp.deptFullName || emp.deptName || emp.deptShortName || emp.orgNamePath || emp.orgName || emp.department || '',
|
||||
organization: emp.orgNamePath || emp.orgName || '',
|
||||
email: emp.email || emp.mail || '',
|
||||
phone: emp.mobile || emp.phone || '',
|
||||
raw: emp,
|
||||
token,
|
||||
},
|
||||
result,
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ export class UdsValidator {
|
|||
this.empNoHeader = (config.empNoHeader || 'X-Emp-No').toLowerCase()
|
||||
this.authValueHeader = (config.authValueHeader || 'X-Auth-Value').toLowerCase()
|
||||
this.langIdHeader = (config.langIdHeader || 'X-Lang-Id').toLowerCase()
|
||||
this.authMode = config.authMode || 'trust'
|
||||
this.authMode = config.authMode || 'token+profile'
|
||||
this.hrApiUrl = config.hrApiUrl || 'https://icosg.dt.zte.com.cn/ihol/usercenter/pginfo/usercenter/plain/queryPersonGeneralInfo'
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +149,12 @@ export class UdsValidator {
|
|||
} else {
|
||||
userData = {
|
||||
userId: credentialsOrResponse.empNo,
|
||||
username: credentialsOrResponse.empNo,
|
||||
username: credentialsOrResponse.username || credentialsOrResponse.empNo,
|
||||
displayName: credentialsOrResponse.displayName || credentialsOrResponse.username || credentialsOrResponse.empNo,
|
||||
department: credentialsOrResponse.department || '',
|
||||
organization: credentialsOrResponse.organization || '',
|
||||
email: credentialsOrResponse.email || '',
|
||||
phone: credentialsOrResponse.phone || '',
|
||||
empNo: credentialsOrResponse.empNo,
|
||||
token: credentialsOrResponse.token,
|
||||
lang: credentialsOrResponse.lang,
|
||||
|
|
@ -173,7 +178,7 @@ export class UdsValidator {
|
|||
lastActiveAt: now,
|
||||
sessionCreatedAt: now,
|
||||
isAuthenticated: true,
|
||||
authMode: this.authMode,
|
||||
authMode: this.authMode || 'token+profile',
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue