Confirmación de cuenta con código de verificación en AWS Cognito (iOS)
En el artículo anterior implementamos el registro de usuarios con AWS Cognito. Al registrarse, el usuario recibe un email con un código de 6 dígitos para confirmar su cuenta. En este artículo vamos a implementar la pantalla y funcionalidad para que el usuario pueda ingresar ese código y activar su cuenta.
Como siempre, mantendremos la implementación clara, sencilla y accesible. Nos enfocaremos en entender cómo funciona el proceso de confirmación en Cognito.
¿Qué vamos a construir?
Una pantalla de confirmación que permita: – Ingresar el código de 6 dígitos recibido por email – Confirmar el código con AWS Cognito – Reenviar el código si no llegó o expiró – Manejar errores comunes (código incorrecto, expirado) – Verificar que la cuenta quede activada en AWS
Requisitos previos
- Haber completado el artículo anterior (Registro de usuarios)
- Tener un usuario registrado en estado UNCONFIRMED
- Haber recibido el email con el código de verificación
Paso 1: Creando la interfaz de confirmación
Primero, vamos a crear una vista simple y limpia para que el usuario ingrese su código. Creamos un archivo llamado ConfirmCodeView.swift:
import SwiftUI
struct ConfirmCodeView: View {
let email: String // Email del usuario que se registró
@State private var code: String = ""
@State private var isLoading: Bool = false
@State private var message: String = ""
@State private var showAlert: Bool = false
var body: some View {
VStack(spacing: 20) {
// Título
Text("Confirmar Cuenta")
.font(.largeTitle)
.fontWeight(.bold)
.padding(.bottom, 10)
// Instrucciones
Text("Ingresa el código de 6 dígitos que enviamos a:")
.font(.body)
.foregroundColor(.gray)
.multilineTextAlignment(.center)
Text(email)
.font(.body)
.fontWeight(.semibold)
.foregroundColor(.blue)
.padding(.bottom, 20)
// Campo de código
TextField("Código de verificación", text: $code)
.keyboardType(.numberPad)
.textContentType(.oneTimeCode) // Autocompletar desde SMS/Email
.padding()
.background(Color(.systemGray6))
.cornerRadius(10)
.multilineTextAlignment(.center)
.font(.title2)
.disabled(isLoading)
// Mensaje de estado
if !message.isEmpty {
Text(message)
.font(.caption)
.foregroundColor(.gray)
.multilineTextAlignment(.center)
.padding(.horizontal)
}
// Botón de Confirmar
Button(action: {
confirmCode()
}) {
if isLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.frame(maxWidth: .infinity)
.padding()
} else {
Text("Confirmar Código")
.fontWeight(.semibold)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
}
}
.background(isLoading ? Color.gray : Color.blue)
.cornerRadius(10)
.disabled(isLoading || code.isEmpty)
.padding(.top, 20)
// Botón de Reenviar código
Button(action: {
resendCode()
}) {
Text("Reenviar código")
.font(.body)
.foregroundColor(.blue)
}
.disabled(isLoading)
.padding(.top, 10)
Spacer()
}
.padding(.horizontal, 30)
.padding(.top, 50)
.alert("Confirmación", isPresented: $showAlert) {
Button("OK", role: .cancel) { }
} message: {
Text(message)
}
}
// MARK: - Función de Confirmación
private func confirmCode() {
// Validación básica
guard !code.isEmpty else {
message = "Por favor ingresa el código"
showAlert = true
return
}
guard code.count == 6 else {
message = "El código debe tener 6 dígitos"
showAlert = true
return
}
// Aquí irá la lógica de confirmación con AWS Cognito
isLoading = true
message = "Confirmando..."
// Por ahora, solo simulamos
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
isLoading = false
message = "Código confirmado exitosamente"
showAlert = true
}
}
// MARK: - Función de Reenvío
private func resendCode() {
// Aquí irá la lógica para reenviar el código
message = "Código reenviado. Revisa tu email."
showAlert = true
}
}
¿Qué hace este código?
Parámetros de entrada: – email: Recibe el email del usuario para mostrarlo en pantalla
Estados locales (@State): – code: Almacena el código de 6 dígitos que ingresa el usuario – isLoading: Controla el indicador de carga – message: Mensajes para el usuario – showAlert: Controla cuándo mostrar alertas
Elementos de la interfaz:
1. Título: “Confirmar Cuenta”
2. Instrucciones: Texto explicativo con el email del usuario
3. Campo de código: – .keyboardType(.numberPad): Teclado numérico – .textContentType(.oneTimeCode): iOS puede autocompletar desde emails/SMS – .multilineTextAlignment(.center): Código centrado – .font(.title2): Texto grande y legible
4. Botón “Confirmar Código”: Con spinner cuando está procesando
5. Botón “Reenviar código”: Por si el código no llegó o expiró
Validaciones básicas: – Verifica que el código no esté vacío – Verifica que tenga exactamente 6 dígitos
Por ahora: Las funciones confirmCode() y resendCode() solo simulan el comportamiento. En el siguiente paso las conectaremos con AWS Cognito.
Paso 2: Navegación automática desde el registro
Para que el usuario llegue a esta pantalla de confirmación después de registrarse, necesitamos configurar la navegación. Vamos a hacer que después de un registro exitoso, la app navegue automáticamente a ConfirmCodeView.
2.1 Actualizar RegisterView
Primero, agregamos los estados necesarios para controlar la navegación en RegisterView.swift:
struct RegisterView: View {
@State private var email: String = ""
@State private var password: String = ""
@State private var confirmPassword: String = ""
@State private var isLoading: Bool = false
@State private var message: String = ""
@State private var showAlert: Bool = false
@State private var navigateToConfirmation: Bool = false // Nuevo
@State private var registeredEmail: String = "" // Nuevo
private let authService = CognitoAuthService()
¿Qué agregamos? – navigateToConfirmation: Controla cuándo navegar a la pantalla de confirmación – registeredEmail: Guarda el email del usuario para pasarlo a ConfirmCodeView
2.2 Agregar navigationDestination
Al final de la vista, antes del cierre del último }, agregamos el modifier de navegación:
.alert("Registro", isPresented: $showAlert) {
Button("OK", role: .cancel) { }
} message: {
Text(message)
}
.navigationDestination(isPresented: $navigateToConfirmation) {
ConfirmCodeView(email: registeredEmail)
}
}
¿Qué hace .navigationDestination? – Cuando navigateToConfirmation es true, navega a ConfirmCodeView – Pasa el registeredEmail como parámetro
2.3 Actualizar la función de registro
Modificamos la parte donde manejamos el registro exitoso:
Task {
do {
let result = try await authService.signUp(email: email, password: password)
await MainActor.run {
isLoading = false
message = result
// Guardar el email antes de limpiar
registeredEmail = email
// Limpiar campos
email = ""
password = ""
confirmPassword = ""
// Navegar a pantalla de confirmación
navigateToConfirmation = true
}
} catch {
// ... manejo de errores
}
}
¿Qué cambió? – Ahora guarda el email en registeredEmail antes de limpiarlo – Activa navigateToConfirmation = true para disparar la navegación – Ya no muestra una alerta de éxito, sino que navega directamente
2.4 Actualizar ContentView
Para que la navegación funcione, necesitamos envolver RegisterView en un NavigationStack:
struct ContentView: View {
var body: some View {
NavigationStack {
RegisterView()
}
}
}
Paso 3: Probando el flujo completo
Ahora si ejecutas la app en el simulador:
- Registro:
- Ingresa tu email real
- Ingresa una contraseña válida (ej: 1@wAs1234)Confirma la contraseña
- Click en “Registrarse”
- Navegación automática:
- Después de registrarse exitosamente
- La app automáticamente navega a ConfirmCodeView
- Verás tu email mostrado en la pantalla
- Pantalla de confirmación:
- Campo para ingresar código de 6 dígitos
- Botón “Confirmar Código”
- Botón “Reenviar código”
Nota: Por ahora los botones solo simulan el comportamiento (el de confirmación espera 2 segundos y muestra un mensaje). En el siguiente paso los conectaremos con AWS Cognito para que funcionen de verdad.

Paso 4: Implementando la confirmación real con AWS Cognito
Ahora vamos a conectar los botones con AWS Cognito para que confirmen y reenvíen códigos de verdad.
4.1 Agregar métodos al servicio de autenticación
Abrimos CognitoAuthService.swift y agregamos dos nuevos métodos después del método signUp():
Método 1: Confirmar código
// MARK: - Confirmación de Usuario
func confirmSignUp(email: String, code: String) async throws -> String {
guard let client = cognitoClient else {
throw NSError(domain: "CognitoAuthService", code: -1,
userInfo: [NSLocalizedDescriptionKey: "Cliente de Cognito no inicializado"])
}
// Crear la solicitud de confirmación
let confirmInput = ConfirmSignUpInput(
clientId: clientId,
confirmationCode: code,
username: email
)
do {
let response = try await client.confirmSignUp(input: confirmInput)
print("✅ Código confirmado exitosamente")
print("Response: \(response)")
return "Cuenta confirmada exitosamente. Ahora puedes iniciar sesión"
} catch {
// Manejar errores de Cognito
print("❌ Error al confirmar: \(error)")
let errorMessage = error.localizedDescription
if errorMessage.contains("CodeMismatchException") {
throw NSError(domain: "CognitoAuthService", code: 2001,
userInfo: [NSLocalizedDescriptionKey: "Código incorrecto. Verifica e intenta de nuevo"])
} else if errorMessage.contains("ExpiredCodeException") {
throw NSError(domain: "CognitoAuthService", code: 2002,
userInfo: [NSLocalizedDescriptionKey: "El código ha expirado. Solicita uno nuevo"])
} else {
throw NSError(domain: "CognitoAuthService", code: 2000,
userInfo: [NSLocalizedDescriptionKey: "Error al confirmar: \(errorMessage)"])
}
}
}
Qué hace este método? – Recibe el email del usuario y el code de 6 dígitos – Crea un ConfirmSignUpInput con estos datos – Llama a client.confirmSignUp() de AWS Cognito – Si es exitoso, retorna mensaje de confirmación – Si hay error, identifica el tipo: – CodeMismatchException: Código incorrecto – ExpiredCodeException: Código expirado – Otros errores genéricos

Método 2: Reenviar código
// MARK: - Reenviar Código de Confirmación
func resendConfirmationCode(email: String) async throws -> String {
guard let client = cognitoClient else {
throw NSError(domain: "CognitoAuthService", code: -1,
userInfo: [NSLocalizedDescriptionKey: "Cliente de Cognito no inicializado"])
}
// Crear la solicitud de reenvío
let resendInput = ResendConfirmationCodeInput(
clientId: clientId,
username: email
)
do {
let response = try await client.resendConfirmationCode(input: resendInput)
print("✅ Código reenviado exitosamente")
print("Destino: \(response.codeDeliveryDetails?.destination ?? "N/A")")
return "Código reenviado. Revisa tu email"
} catch {
// Manejar errores de Cognito
print("❌ Error al reenviar código: \(error)")
throw NSError(domain: "CognitoAuthService", code: 3000,
userInfo: [NSLocalizedDescriptionKey: "Error al reenviar código: \(error.localizedDescription)"])
}
}
¿Qué hace este método? – Recibe solo el email del usuario – Crea un ResendConfirmationCodeInput – Llama a client.resendConfirmationCode() de AWS Cognito – AWS envía un nuevo código al email del usuario – Retorna mensaje de confirmación
4.2 Actualizar ConfirmCodeView
Ahora conectamos la vista con el servicio. Primero agregamos la instancia del servicio en ConfirmCodeView.swift:
struct ConfirmCodeView: View {
let email: String
@State private var code: String = ""
@State private var isLoading: Bool = false
@State private var message: String = ""
@State private var showAlert: Bool = false
private let authService = CognitoAuthService() // Nuevo
Luego actualizamos la función confirmCode():
// MARK: - Función de Confirmación
private func confirmCode() {
// Validación básica
guard !code.isEmpty else {
message = "Por favor ingresa el código"
showAlert = true
return
}
guard code.count == 6 else {
message = "El código debe tener 6 dígitos"
showAlert = true
return
}
// Confirmar con AWS Cognito
isLoading = true
message = "Confirmando..."
Task {
do {
let result = try await authService.confirmSignUp(email: email, code: code)
await MainActor.run {
isLoading = false
message = result
showAlert = true
// Limpiar el código
code = ""
}
} catch {
await MainActor.run {
isLoading = false
message = error.localizedDescription
showAlert = true
}
}
}
}
¿Qué cambió? – Ya no es una simulación – Llama al método real authService.confirmSignUp() – Usa Task para llamadas asíncronas – Maneja errores reales de AWS Cognito – Limpia el código después de confirmación exitosa

Y actualizamos la función resendCode():
// MARK: - Función de Reenvío
private func resendCode() {
isLoading = true
message = "Reenviando código..."
Task {
do {
let result = try await authService.resendConfirmationCode(email: email)
await MainActor.run {
isLoading = false
message = result
showAlert = true
}
} catch {
await MainActor.run {
isLoading = false
message = error.localizedDescription
showAlert = true
}
}
}
}
¿Qué cambió? – Llama al método real authService.resendConfirmationCode() – Muestra mensajes reales de AWS Cognito
4.3 Probando la confirmación real
Ejecuta la app y prueba el flujo completo:
- Registro:
- Ingresa tu email
- Ingresa contraseña válida
- Click en “Registrarse”
- Navegación automática:
- La app navega a pantalla de confirmación
- Verás tu email mostrado
- Confirmación:
- Ingresa el código de 6 dígitos de tu email
- Click en “Confirmar Código”
- Verás: “Cuenta confirmada exitosamente. Ahora puedes iniciar sesión”
Errores posibles: – Si ingresas código incorrecto: “Código incorrecto. Verifica e intenta de nuevo” – Si el código expiró: “El código ha expirado. Solicita uno nuevo”
Probando “Reenviar código”: – Click en “Reenviar código” – Recibirás un nuevo código por email – Verás: “Código reenviado. Revisa tu email”
4.4 Verificar en AWS Console
Para confirmar que todo funcionó:
- Ve a AWS Console → Cognito → User pools
- Abre tu User Pool
- Ve a “Users”
- Busca tu usuario
Deberías ver: – Account status: CONFIRMED ✅ – Email verified: Yes ✅
Si antes estaba UNCONFIRMED, ahora está CONFIRMED. La cuenta está activada y lista para iniciar sesión.
Conclusión
En este artículo implementamos el flujo completo de confirmación de cuenta con AWS Cognito:
✅ Interfaz de confirmación: Una pantalla simple y limpia para ingresar el código de 6 dígitos
✅ Navegación automática: Después del registro, la app lleva al usuario directamente a confirmar su cuenta
✅ Confirmación real: Conectamos la app con AWS Cognito usando confirmSignUp()
✅ Reenvío de código: Implementamos la opción de solicitar un nuevo código si el primero expiró o no llegó
✅ Manejo de errores: Distinguimos entre código incorrecto, código expirado, y otros errores
✅ Verificación en AWS: Confirmamos que el usuario pasa de UNCONFIRMED a CONFIRMED
Ahora los usuarios pueden registrarse y confirmar sus cuentas de forma completa. En el siguiente artículo vamos a implementar el inicio de sesión (Sign In) para que los usuarios confirmados puedan autenticarse y obtener sus tokens de acceso.
Reflexión final
Te invito a que me sigas en LinkedIn donde comparto contenido relacionado a desarrollo de software, buenas prácticas, y tecnologías AWS. También puedes encontrarme en Medium y explorar mis proyectos en GitHub. Si tienes preguntas o sugerencias, no dudes en conectar – siempre estoy abierto a intercambiar ideas y aprender junto a la comunidad.
Sobre las validaciones
Esta serie de artículos se enfoca en entender cómo funciona AWS Cognito en su forma más pura, por lo que intencionalmente he mantenido las validaciones al mínimo. En un proyecto real de producción, deberías considerar:
- Validaciones más robustas de email (formato, dominios válidos)
- Validación de contraseña en el cliente antes de enviar a AWS
- Manejo de estados de UI más sofisticado (loading, success, error)
- Reintentos automáticos en caso de fallos de red
- Logs y monitoreo de errores
- Timeout en operaciones de red
- Validación de rate limiting
- Ofuscación de credenciales en el código
- Gestión segura de tokens
- Testing unitario y de integración
El objetivo es que entiendas primero cómo funciona Cognito, y luego puedas aplicar las mejores prácticas de desarrollo según las necesidades de tu proyecto.