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

Flutter自定义登录页对接Auth0实现嵌入式登录(无需通用登录重定向)

Flutter 自定义Auth0登录页实现方案

1. Auth0控制台配置

首先在Auth0应用的Advanced Settings -> Grant Types中,勾选Password和Password Realm(使用数据库连接时需开启),确保允许密码授权流。

2. 依赖配置

在pubspec.yaml中添加HTTP请求依赖:

dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0
  # 可选:用于安全存储token
  flutter_secure_storage: ^9.0.0

3. 实现Auth0登录请求

封装Auth0服务类,发送登录请求到Auth0的/oauth/token端点:

import 'dart:convert';
import 'package:http/http.dart' as http;

class Auth0Service {
  // 替换为你的Auth0域名、客户端ID和API标识符
  final String _domain = 'your-auth0-domain';
  final String _clientId = 'your-client-id';
  final String _audience = 'your-api-audience';

  Future<Map<String, dynamic>> login(String username, String password) async {
    final url = Uri.parse('https://$_domain/oauth/token');
    
    final response = await http.post(
      url,
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        'grant_type': 'password',
        'username': username,
        'password': password,
        'client_id': _clientId,
        'audience': _audience,
        'scope': 'openid profile email offline_access'
      }),
    );

    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception(jsonDecode(response.body)['error_description'] ?? '登录失败');
    }
  }

  // 可选:获取用户信息
  Future<Map<String, dynamic>> getUserInfo(String accessToken) async {
    final url = Uri.parse('https://$_domain/userinfo');
    
    final response = await http.get(
      url,
      headers: {'Authorization': 'Bearer $accessToken'},
    );

    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    } else {
      throw Exception('获取用户信息失败');
    }
  }
}

4. 自定义登录UI实现

编写Flutter登录页面,处理输入和登录逻辑:

import 'package:flutter/material.dart';

class CustomLoginPage extends StatefulWidget {
  @override
  _CustomLoginPageState createState() => _CustomLoginPageState();
}

class _CustomLoginPageState extends State<CustomLoginPage> {
  final _usernameController = TextEditingController();
  final _passwordController = TextEditingController();
  final _auth0Service = Auth0Service();
  bool _isLoading = false;

  void _submitLogin() async {
    setState(() => _isLoading = true);
    try {
      final authResult = await _auth0Service.login(
        _usernameController.text.trim(),
        _passwordController.text.trim(),
      );
      // 存储token(推荐用flutter_secure_storage加密存储)
      // 跳转到主页
      Navigator.pushReplacementNamed(context, '/home');
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(e.toString())),
      );
    } finally {
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('自定义登录')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: _usernameController,
              decoration: const InputDecoration(
                labelText: '用户名/邮箱',
                border: OutlineInputBorder(),
              ),
              enabled: !_isLoading,
            ),
            const SizedBox(height: 16),
            TextField(
              controller: _passwordController,
              decoration: const InputDecoration(
                labelText: '密码',
                border: OutlineInputBorder(),
              ),
              obscureText: true,
              enabled: !_isLoading,
            ),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: _isLoading ? null : _submitLogin,
              style: ElevatedButton.styleFrom(
                minimumSize: const Size(double.infinity, 50),
              ),
              child: _isLoading
                  ? const CircularProgressIndicator(color: Colors.white)
                  : const Text('登录', style: TextStyle(fontSize: 16)),
            ),
          ],
        ),
      ),
    );
  }
}

注意事项

  • 密码授权流(Password Grant)不推荐用于公开客户端(如移动端应用),因为客户端ID存在被反编译泄露的风险;若需更高安全性,可考虑Auth0的授权码流+PKCE方案结合自定义UI(需自行处理授权流程跳转)。
  • 务必安全存储access_token和refresh_token,避免明文存储。
  • 若使用数据库连接,需确保Auth0控制台中已启用对应连接并添加测试用户。

内容的提问来源于stack exchange,提问作者Bhargava Shankar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:10:13