session()->get('dingtalk_identity_id'); $user = $request->user(); if (! $identityId) { if (! $user || $user->created_from !== 'dingtalk_roster') { return $next($request); } // A password reset must not bypass HR eligibility for provisioned // accounts. Apply the same boundary after every sign-in method. $identityId = $user->dingtalkIdentity?->id; $request->session()->put('dingtalk_identity_id', $identityId); $request->session()->forget('dingtalk_roster_checked_at'); } $identity = $user ? DingTalkIdentity::query()->whereKey($identityId)->where('user_id', $user->id)->first() : null; if ($user && $user->enabled && $identity) { $checkedAt = (int) $request->session()->get('dingtalk_roster_checked_at', 0); if ($checkedAt > now()->timestamp - 300) { return $next($request); } try { $eligible = app(FinanceRoster::class)->findEligible((string) $identity->dingtalk_user_id); } catch (Throwable) { // Retain the session during an HR outage, but do not serve data // using an expired authorization decision. return response(trans('auth.dingtalk.roster_unavailable'), 503) ->header('Cache-Control', 'no-store'); } if ($eligible) { $request->session()->put('dingtalk_roster_checked_at', now()->timestamp); return $next($request); } } app(DingTalkClosedAccess::class)->audit('session_revoked', 'denied', $user?->id, null, $identity?->union_id, $request, [ 'reason' => ! $user?->enabled ? 'local_account_ineligible' : (! $identity ? 'binding_revoked' : 'outside_finance_roster'), ]); auth()->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); if ($request->expectsJson()) { return response()->json(['message' => trans('auth.dingtalk.session_expired')], 401) ->header('Cache-Control', 'no-store'); } return redirect()->route('login') ->with('dingtalk_error', trans('auth.dingtalk.session_expired')); } }