You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core复杂2FA认证实现需求:Google登录后PIN二次验证

Complete Implementation Guide for Google Login + PIN Verification + Idle Logout

Great question—let's walk through how to implement this complete flow, covering all your requirements with practical code examples and best practices:

1. Post-Google Login Redirect to PIN Page

First, after a user completes Google login, you need to check if they've ever verified their PIN before. If it's their first time, redirect them to the PIN input page.

Here's how you might handle this in the Google login callback (example in ASP.NET Core; adjust for your framework):

public async Task<IActionResult> GoogleLoginCallback()
{
    var authResult = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
    var userEmail = authResult.Principal.FindFirstValue(ClaimTypes.Email);
    
    // Fetch or create the user record in your database
    var user = await _userRepo.GetByEmailAsync(userEmail);
    if (user == null)
    {
        user = new User 
        { 
            Email = userEmail, 
            IsPinVerified = false,
            HashedPin = "<your-pre-stored-hashed-pin>" // Or set this via admin flow
        };
        await _userRepo.AddAsync(user);
    }
    
    if (!user.IsPinVerified)
    {
        // Store the user's ID in session to link PIN input to their account
        HttpContext.Session.SetInt32("PendingPinUserId", user.Id);
        return RedirectToAction("EnterPin", "Auth");
    }
    
    // User is already verified—sign them in and send to home
    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, authResult.Principal);
    return RedirectToAction("Index", "Home");
}

2. PIN Validation & Access Control

Next, build the PIN input page and backend validation. If the PIN doesn't match, send the user back to try again. Once valid, mark their account as verified and sign them in.

Backend Validation Action

[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> EnterPin(PinInputModel model)
{
    if (!ModelState.IsValid)
        return View(model);
    
    var pendingUserId = HttpContext.Session.GetInt32("PendingPinUserId");
    if (pendingUserId == null)
        return RedirectToAction("GoogleLogin");
    
    var user = await _userRepo.GetByIdAsync(pendingUserId.Value);
    if (user == null)
        return RedirectToAction("GoogleLogin");
    
    // Critical: Always use hashing to compare PINs (never store plain text!)
    bool isPinValid = BCrypt.Net.BCrypt.Verify(model.Pin, user.HashedPin);
    if (!isPinValid)
    {
        ModelState.AddModelError("Pin", "Invalid PIN. Please try again.");
        return View(model);
    }
    
    // Mark user as verified and complete the login flow
    user.IsPinVerified = true;
    await _userRepo.UpdateAsync(user);
    
    // Retrieve the Google auth principal and sign the user in
    var googlePrincipal = await GetGooglePrincipalForUser(user.Email);
    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, googlePrincipal);
    
    // Clear the pending session to prevent reuse
    HttpContext.Session.Remove("PendingPinUserId");
    
    return RedirectToAction("Index", "Home");
}

Restrict Access to Verified Users

Use an authorization policy to block un-verified users from accessing most pages:

// Configure policy in Startup/Program.cs
services.AddAuthorization(options =>
{
    options.AddPolicy("PinVerified", policy =>
        policy.RequireClaim("IsPinVerified", "true"));
});

// Apply to protected controllers/actions
[Authorize(Policy = "PinVerified")]
public class DashboardController : Controller
{
    // All actions here require PIN verification
}

// Keep the PIN input page accessible to anonymous users
[AllowAnonymous]
public class AuthController : Controller
{
    public IActionResult EnterPin() => View();
}

3. Idle Timeout & Logout on Browser Close

To handle idle logout and automatic cleanup when the browser closes:

Set up your authentication cookie to expire after X minutes of inactivity, and use a session cookie (non-persistent) so it's cleared when the browser closes:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.ExpireTimeSpan = TimeSpan.FromMinutes(X); // Your idle timeout value
        options.SlidingExpiration = true; // Reset timeout on user activity
        options.Cookie.IsEssential = true;
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Use in production
        options.Cookie.SameSite = SameSiteMode.Strict;
        
        // Handle timeout redirects
        options.Events.OnRedirectToLogin = context =>
        {
            context.HttpContext.Session.Clear();
            return base.OnRedirectToLogin(context);
        };
    });

Frontend Idle Monitor

Add JavaScript to detect user inactivity and trigger a logout request proactively:

const IDLE_TIMEOUT_MINUTES = X; // Match backend value
let idleTimer;

function resetIdleTimer() {
    clearTimeout(idleTimer);
    idleTimer = setTimeout(() => {
        // Send POST request to logout endpoint
        fetch('/Auth/Logout', {
            method: 'POST',
            headers: { 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value }
        })
        .then(() => window.location.href = '/Auth/GoogleLogin');
    }, IDLE_TIMEOUT_MINUTES * 60 * 1000);
}

// Listen for user activity to reset the timer
document.addEventListener('mousemove', resetIdleTimer);
document.addEventListener('keydown', resetIdleTimer);
document.addEventListener('click', resetIdleTimer);
document.addEventListener('scroll', resetIdleTimer);

// Initialize the timer when the page loads
resetIdleTimer();

Key Best Practices

  • Never store plain-text PINs: Always hash PINs using a strong hashing algorithm like BCrypt before saving to your database.
  • Shorten pending PIN sessions: Set a short expiration on the PendingPinUserId session to prevent stale sessions from being reused.
  • Test edge cases: Verify what happens if a user closes the browser mid-PIN entry—their session will expire, so they'll have to restart the login flow, which is secure.
  • Secure cookies: Use HttpOnly, Secure, and SameSite flags on your authentication cookies to prevent XSS and CSRF attacks.

内容的提问来源于stack exchange,提问作者Barak Gall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:14:29