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

在iOS端不使用Cognito托管UI实现Amazon Cognito社交提供商关联的问题

在iOS端不使用Cognito托管UI实现Amazon Cognito社交提供商关联的问题

我完全懂你的困扰——用户已经用手机号好好登录了,结果要绑个社交账号还得先登出,这流程走起来用户肯定会懵。别发愁,咱们可以通过自定义流程绕开托管UI的限制,直接把社交账号和已登录的Cognito账号关联上,下面是具体的实现思路和代码示例:

核心思路

咱们不依赖Amplify的signInWithWebUI(),而是分三步走:

  • 用原生社交SDK(比如GoogleSignIn、Facebook Login、Sign in with Apple)获取社交平台的认证Token
  • 拿到当前已登录Cognito用户的唯一标识(sub)和会话信息
  • 通过后端Lambda函数调用Cognito的管理员API,把社交账号和现有Cognito账号关联起来(因为前端不能直接调用需要管理员权限的API)

具体步骤

1. 集成原生社交SDK并获取认证Token

这一步直接用各平台的官方SDK,完全绕开Cognito的托管UI。举几个常用平台的例子:

Google 示例

import GoogleSignIn

func fetchGoogleIdToken() async throws -> String {
    guard let rootVC = UIApplication.shared.connectedScenes
            .compactMap({ $0 as? UIWindowScene })
            .first?.windows
            .first?.rootViewController else {
        throw NSError(domain: "SocialAuth", code: -1, userInfo: [NSLocalizedDescriptionKey: "找不到可展示的视图控制器"])
    }
    
    let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootVC)
    guard let idToken = signInResult.user.idToken?.tokenString else {
        throw NSError(domain: "SocialAuth", code: -2, userInfo: [NSLocalizedDescriptionKey: "无法获取Google ID Token"])
    }
    return idToken
}

Apple 示例

import AuthenticationServices

func fetchAppleIdToken() async throws -> String {
    let provider = ASAuthorizationAppleIDProvider()
    let request = provider.createRequest()
    request.requestedScopes = [.fullName, .email]
    
    let authorizationController = ASAuthorizationController(authorizationRequests: [request])
    let result = try await authorizationController.performAuthorization()
    
    guard let appleIDCredential = result.credential as? ASAuthorizationAppleIDCredential,
          let idTokenData = appleIDCredential.identityToken,
          let idToken = String(data: idTokenData, encoding: .utf8) else {
        throw NSError(domain: "SocialAuth", code: -3, userInfo: [NSLocalizedDescriptionKey: "无法获取Apple ID Token"])
    }
    return idToken
}

2. 获取当前已登录Cognito用户的信息

用户已经用手机号登录了,咱们直接从Amplify获取当前会话的用户ID(sub):

import Amplify

func getCurrentCognitoUserSub() async throws -> String {
    let session = try await Amplify.Auth.fetchAuthSession()
    guard let cognitoSession = session as? AuthCognitoSession,
          let userSub = cognitoSession.userSubResult.value else {
        throw NSError(domain: "CognitoAuth", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法获取当前登录用户的ID"])
    }
    return userSub
}

3. 通过后端Lambda关联账号

因为Cognito的AdminLinkProviderForUserAPI需要管理员权限,不能在前端直接调用,所以咱们得写个Lambda函数来做这件事:

Lambda函数核心代码(Python示例)

import boto3
import jwt

cognito_client = boto3.client('cognito-idp')

def lambda_handler(event, context):
    user_pool_id = "你的Cognito用户池ID"
    target_user_sub = event['user_sub']  # 前端传过来的当前Cognito用户sub
    provider_name = event['provider_name']  # 比如"Google"、"Facebook"、"SignInWithApple"
    provider_token = event['provider_token']  # 前端传过来的社交Token

    # 解析社交Token获取用户唯一ID,不同提供商解析方式不同
    if provider_name == "Google":
        decoded_token = jwt.decode(provider_token, options={"verify_signature": False})
        provider_user_id = decoded_token['sub']
    elif provider_name == "Facebook":
        # Facebook需要用access token调用其API获取用户ID,或者直接用token解析
        decoded_token = jwt.decode(provider_token, options={"verify_signature": False})
        provider_user_id = decoded_token['id']
    elif provider_name == "SignInWithApple":
        decoded_token = jwt.decode(provider_token, options={"verify_signature": False})
        provider_user_id = decoded_token['sub']
    else:
        raise ValueError("不支持的提供商")

    response = cognito_client.admin_link_provider_for_user(
        UserPoolId=user_pool_id,
        DestinationUser={
            'ProviderName': 'Cognito',
            'ProviderAttributeName': 'sub',
            'ProviderAttributeValue': target_user_sub
        },
        SourceUser={
            'ProviderName': provider_name,
            'ProviderAttributeName': 'Cognito_Subject',
            'ProviderAttributeValue': provider_user_id
        }
    )
    return {'statusCode': 200, 'body': '账号关联成功'}

然后前端通过API网关调用这个Lambda函数,把需要的参数传过去就行:

func linkSocialAccountToCurrentUser(providerName: String, providerToken: String) async throws {
    let userSub = try await getCurrentCognitoUserSub()
    let parameters: [String: Any] = [
        "user_sub": userSub,
        "provider_name": providerName,
        "provider_token": providerToken
    ]
    
    // 这里调用你的API网关端点,发送POST请求
    let url = URL(string: "你的API网关URL")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = try JSONSerialization.data(withJSONObject: parameters)
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    
    let (_, response) = try await URLSession.shared.data(for: request)
    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
        throw NSError(domain: "LinkAccountError", code: -1, userInfo: [NSLocalizedDescriptionKey: "账号关联失败"])
    }
}

注意事项

  • 要确保你的Cognito用户池已经开启了对应的社交提供商的登录选项
  • 解析社交Token获取用户ID的时候,要注意不同提供商的字段差异:Google是sub,Facebook是id,Apple是sub
  • Lambda函数要配置足够的权限(需包含Cognito的cognito-idp:AdminLinkProviderForUser权限)

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 10:43:11