J'essaie d'implémenter les nouveaux groupes d'utilisateurs AWS Cognito dans mon application iOS (Swift), mais j'ai du mal à faire en sorte que le processus de connexion fonctionne. J'essaie essentiellement de suivre l'exemple disponible ici .
C'est ce que j'ai jusqu'ici:
AppDéléguer:
class AppDelegate: UIResponder, UIApplicationDelegate, AWSCognitoIdentityInteractiveAuthenticationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = serviceConfiguration
let configurationUserPool = AWSCognitoIdentityUserPoolConfiguration(
clientId: "###",
clientSecret: "#########",
poolId: "###")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: configurationUserPool, forKey: "UserPool")
self.userPool = AWSCognitoIdentityUserPool(forKey: "UserPool")
self.userPool!.delegate = self
return true
}
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let logInNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInNavigationController") as! UINavigationController
dispatch_async(dispatch_get_main_queue(), {
self.window?.rootViewController = logInNavigationController
})
let logInViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInViewController") as! LogInViewController
return logInViewController
}
}
LogInViewController:
class LogInViewController: UIViewController, AWSCognitoIdentityPasswordAuthentication {
var usernameText : String?
var passwordAuthenticationCompletion = AWSTaskCompletionSource()
func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
dispatch_async(dispatch_get_main_queue(), {
if self.usernameText == nil {
self.usernameText = authenticationInput.lastKnownUsername
}
})
}
func didCompletePasswordAuthenticationStepWithError(error: NSError) {
dispatch_async(dispatch_get_main_queue(), {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let mainNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("MainNavigationController") as! UINavigationController
(UIApplication.sharedApplication().delegate as! AppDelegate).window?.rootViewController = mainNavigationController
})
}
func logInButtonPressed() {
self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails(username: emailTextField.text, password: passwordTextField.text))
}
}
Rien ne semble se produire lorsque je clique sur le bouton de connexion, mais si je le clique à nouveau, je reçois une exception NSInternalInconsistencyException (qui, à mon avis, est due au fait que le résultat AWSTask a déjà été défini).
Toute aide à ce sujet serait appréciée. J'utilise le kit AWS SDK pour iOS version 2.4.1.
METTRE À JOUR:
Ce n’est pas une solution à mon problème initial, mais j’ai réussi à faire fonctionner les groupes d’utilisateurs en utilisant la méthode de connexion explicite plutôt que la méthode de délégué (voir this page pour plus de détails). Voici le code de mon SignInViewController:
class SignInViewController: UIViewController {
@IBAction func signInButtonTouched(sender: UIButton) {
if (emailTextField.text != nil) && (passwordTextField.text != nil) {
let user = (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!.getUser(emailTextField.text!)
user.getSession(emailTextField.text!, password: passwordTextField.text!, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: {
(task:AWSTask!) -> AnyObject! in
if task.error == nil {
// user is logged in - show logged in UI
} else {
// error
}
return nil
})
} else {
// email or password not set
}
}
}
Ensuite, pour utiliser un service AWS (qui dans mon cas est situé dans une région différente de Cognito), j'ai créé un nouveau fournisseur d'informations d'identification à l'aide du pool d'utilisateurs:
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "###", identityProviderManager: (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!)
let serviceConfiguration = AWSServiceConfiguration(region: .APNortheast1, credentialsProvider: credentialsProvider)
AWSLambdaInvoker.registerLambdaInvokerWithConfiguration(serviceConfiguration, forKey: "Lambda")
let lambdaInvoker = AWSLambdaInvoker(forKey: "Lambda")
Un autre problème est que je voyais cette erreur chaque fois que je lançais l'application: "Impossible de trouver les valeurs" AWSDefaultRegionType "," AWSCognitoRegionType "et" AWSCognitoIdentityPoolId "valides dans info.plist.". Cela semble être lié à Fabric, que j'utilise pour suivre les collisions. J'ai résolu ce problème en modifiant cette ligne dans AppDelegate:
Fabric.with([AWSCognito.self, Crashlytics.self])
pour ça:
Fabric.with([Crashlytics.self])
J'espère que ça aidera quelqu'un d'autre.
Mise à jour 6: (et vraiment final cette fois)
Il est à noter que (enfin) AWS a fait construire par AWS Mobile Hub une très belle application de démonstration qui inclut les pools d'utilisateurs en tant que SignInProvider (avec Google et Facebook également). L'architecture est (à mon avis) excellente (ils ont séparé la gestion des identités et obtenu les informations d'identification de l'authentification)
Mise à jour 5: (et finale)
Il existe un exemple d'implémentation assez complet et une documentation sur son fonctionnement dans cette autre réponse.
iOS - AWS MobileHub se connecter avec le fournisseur authentifié par le développeur
Mise à jour 4:
Si vous souhaitez accéder aux services AWS, d'autres étapes sont nécessaires.
Il s'avère que cela ne vous authentifie pas avec les identités fédérées de Cognito (le nombre de connexions sur le navigateur d'identités reste à 0). Pour résoudre ce problème, vous devez établir un credentialsProvider et faire "credentialsProvider.getIdentityId". Après cela, les connexions seront positives et vous pourrez obtenir des services AWS en fonction de votre rôle authentifié.
Si vous essayez d'effectuer un accès authentifié et non authentifié pour votre application mobile, vous devez créer un AWSAnonymousCredentialsProvider (dans une configuration de service distincte). Ensuite, vous auto.credentialsProvider? .InvalidateCachedTemporaryCredentials () et self.credentialsProvider? .ClearCredentials () lorsque vous vous déconnectez et effectuez à nouveau la procédure getidentityid avec la configuration de service anonyme et vous obtiendrez un identifiant anonyme. (Remarque: j'ai trouvé que, si vous effacez la chaîne de clé du fournisseur de données d'identification, elle commence par un nouvel identifiant chaque fois qu'un utilisateur se déconnecte, ce qui peut graver rapidement vos 50 000 identifiants gratuits.)
Mise à jour 3:
Téléchargement d'un exemple d'application github pour AWS User Pools pour IOS dans Swift.
https://github.com/BruceBuckland/signin
Mise à jour 2:
J'ai enfin réussi à faire fonctionner correctement les groupes d'utilisateurs AWS dans Swift.
Mon problème était que chaque fois que le démarrage de l'authentification a eu lieu, cela était dû à un échec d'authentification dans un contrôleur de vue différent (mon erreur). Je me suis retrouvé avec un groupe d'entre eux qui attendaient des retours d'achèvement qui ne venaient jamais et l'API était "silencieuse" (ne montrait aucune erreur). L'API ne remarque pas qu'elle est lancée plusieurs fois (par un viewController différent à chaque fois), alors elle permet de se connecter sans interruption. Il n’ya pas assez de votre code dans le message original pour voir si vous rencontrez le même problème.
Vous devez faire attention, l'exemple de code AWS (en Objective-C) comporte deux contrôleurs de navigation et le code les réutilise. Je n'aime pas la façon dont l'exemple d'application fait clignoter le contrôleur de vue connecté avant que le délégué à l'authentification ne s'en aille et j'essayais de l'améliorer dans la version Swift et cela causait mon problème.
L'API AWS User Pools est configurée pour fonctionner avec un storyboard ou une structure d'application qui fonctionne de la manière suivante:
1) Votre application suppose qu'elle est connectée, puis déclenche le délégué qui déclenche l'authentification et les écrans de connexion si ce n'est pas le cas.
2) Dans le contrôleur de vue d'origine connecté, pool.currentUser () n'est PAS suffisant pour obtenir l'authentification, l'API ne déclenchera le délégué que si vous faites plus (dans mon cas, user.getDetails ()).
3) L'authentification est complétée par la didCompletePasswordAuthenticationStepWithError. Cette méthode de délégation est appelée si vous obtenez une erreur d'authentification (ou autre) ET si vous vous authentifiez avec succès. En cas d'authentification réussie, NSError est nil, il doit donc être déclaré comme NSError? dans le délégué (cela provoque un avertissement). L'API est bêta, ils vont probablement résoudre ce problème.
4) Un autre petit «casse-tête», cela peut sembler évident pour vous, cela m'a frappé, lorsque vous définissez votre groupe d'utilisateurs dans la console, vous spécifiez les applications autorisées et chacune de ces applications comporte différentes chaînes pour les chaînes d'identifiant client. (Je viens de brancher la même chose dans l'exemple) qui fonctionne mal (mais ne rapporte pas d'erreurs). L'API nécessite des travaux dans le service de reporting. Il est très verbeux quand cela fonctionne, mais ne dit rien si vous lui transmettez les mauvaises chaînes client. En outre, il semble ne rien dire si vous (comme moi) appelez l'API depuis différents contrôleurs de vue. Il s'agissait simplement de prendre chaque nouvelle demande d'authentification d'un contrôleur de vue différent sans rien dire.
Quoi qu'il en soit, cela fonctionne maintenant. J'espère que cela aide à résoudre votre problème.
Mettre à jour:
J'ai enfin eu getPasswordAuthenticationDetails à exécuter.
Il s'avère que l'exécution ne sera exécutée qu'une fois user.getDetails pour l'utilisateur actuel (même s'il n'y a pas d'utilisateur actuel).
Alors
let user = appDelegate.pool!.currentUser()
let details = user!.getDetails()
le rappel getPasswordAuthenticationDetails sera exécuté sur la deuxième ligne.
Il semble que le concept AWS UserPool consiste à écrire une application qui suppose que nous avons un utilisateur connecté. Nous obtenons des détails de cet utilisateur (par exemple dans le contrôleur de vue initial) et le délégué est renvoyé si nous n'avons pas d'utilisateur.Certaines pages de concept importantes manquent dans la documentation AWS pour les pools d'utilisateurs sur IOS. Ces pages sont incluses dans la documentation Android (sinon parallèle). J'admets que j'ai toujours du mal à faire fonctionner les pools d'utilisateurs dans Swift, mais la lecture des sections "Main Classes" et "Key Concepts" de la documentation Android m'a beaucoup clarifiée. Je ne vois pas pourquoi il a été omis du document IOS.
The AWS documentation for User Pools on IOS is missing some important concept pages. Those pages ARE included in the (otherwise parallel) Android documentation. I admit that I am still struggling (days now) with getting User Pools to work in Swift, but reading the "Main Classes" and "Key Concepts" Parts of the Android documentation clarified a lot for me. I can't see why it was omitted from the IOS doc.
J'ajoute simplement mes 2 centimes pour les personnes qui travaillent avec Objective-c et l'exemple d'application CognitoYourUserPoolsSample fourni par Amazon. @ Bruce0 a déjà tout couvert avec sa solution Swift. Mais si vous rencontrez ce problème où getPasswordAuthenticationDetails n'est pas appelé lorsque vous cliquez sur la connexion, c'est que vous n'appelez pas [self.user getDetails] du tout. En effet - getDetails déclenche getPasswordAuthenticationDetails. Si vous regardez de plus près dans l'exemple d'application AWS, ils l'appellent correctement lorsqu'ils lancent l'application dans viewDidLoad du UserDetailTableViewController , qui est le premier contrôleur chargé. Si l'utilisateur n'est pas connecté, la réponse getDetails déclenche en quelque sorte le SignInViewController. C'est ce que je vais expliquer ci-dessous. C'est comme un type "myHomeViewController", dans lequel vous souhaitez afficher des informations relatives à l'utilisateur. Sinon, vous souhaitez afficher l'écran de connexion/inscription par défaut.
En règle générale, connectez-vous et démarrez le groupe d'utilisateurs Cognito dans votre AppDelegate ( didFinishLaunchingWithOptions ) exactement comme ils l'ont fait dans l'exemple d'application. Assurez-vous d’ajouter le AWSCognitoIdentityInteractiveAuthenticationDelegate et implémentez startPasswordAuthentication dans lequel vous ouvrirez votre ViewController de connexion. Laissez l'AppDelegate s'occuper du WHAT_TO_DO_IF_USER_NOT_SIGNED_IN (par exemple, placez le SignInViewController au-dessus), puis concentrez-vous sur le WHEN_DOES_THE_USER_NEEDS_TO_SIGNIN quelque part dans votre application.
Lorsque vous avez besoin de données utilisateur spécifiques, indiquez à l'application qu'il est temps de vérifier si l'utilisateur est connecté (self.user getDetails). Encore une fois, si l'utilisateur n'est pas connecté, AppDelegate sait quoi faire. Il sur-régit l'application et affiche la vue de connexion au-dessus de tout. Donc, cela pourrait être au début (par exemple, Facebook, Twitter, etc.) ou ailleurs (par exemple, Ebay, etc.). Appelez simplement [self.user getDetails] à la fin de viewDidLoad. Cela empêchera le ViewController actuel de s'afficher avant l'étape d'authentification (connexion/inscription) OR charge simplement le ViewController actuel si l'utilisateur est déjà connecté.
Le modèle d'application AWS n'est pas simple, mais il est très simple.
Merci Elliot. J'essaie d'écrire la version Swift de ce code depuis quelques jours maintenant.
J'ai essayé d'utiliser le login explicite en utilisant le code ci-dessous.
@IBAction func signInButtonPressed(sender: AnyObject) {
var emailTextField = "username"
var passwordTextField = "password"
let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = serviceConfiguration
let configurationUserPool = AWSCognitoIdentityUserPoolConfiguration.init(clientId: "####", clientSecret: "#####", poolId: "#####")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: configurationUserPool, forKey: "TestUserPool")
let userPool = AWSCognitoIdentityUserPool(forKey: "TestUserPool")
let user = userPool.getUser(emailTextField)
user.getSession(emailTextField, password: passwordTextField, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: {
(task:AWSTask!) -> AnyObject! in
if task.error == nil {
print("No Error")
print(task.result)
} else {
print("Some Error")
print(task.error)
}
return nil
})
}
Lorsque je fournis des informations d'identification correctes, cela passe au bloc d'erreur. Un code de vérification est envoyé à mon téléphone portable chaque fois que je l'exécute, bien que j'aie déjà vérifié mon utilisateur lors de la procédure d'inscription. Le corps de réponse est
Response body:
{"AuthState":"H4sIAAAAAAAAAAXB3ZJzMAAA0EeqBDvTnfkulhVCpRXyI3dNmI8KzU7bLZ5+z+m3HBjINz2jp6rxB174rmT+agWweHyPLVydEqFXi2o8j9gjTT6XcH1qeA+vWWQVbAMDW6gXvhEYgHOMH3gmg06pNTP61pBaNvO1E3zvEPFaSS2+3ccuQ6qUVvXcYjqBQKFoKvfoJHgLDKJx3VhlkKsIUGs7qbhH6qXZ3a9kl+v0uPEEOWqR0/7gk4T8iiYPm0XBXt59LivPwAGUmSr1RAfDqSz8COhkZcQLFdsev3oGVw3oWTRRXIHuRkTuqYS6/juHBIYRgzTsZ1crqHB5I5OZ2JvaMmB2aKqpS2qYizMqg5KjgqI24DtNGLfXenGu8/+/zU5ZnZlVCXTRNwtKxgXP2k0LJK9T58TCnxxRJtLnQ7AAFD4lZpnWk+dY4fGBCFqZlP4YyUGfqVQ3rW/i/PgJPnd8WN8fw/Hr5D0OChfhfCleb290yaV/AXf4itllINJONfv3B7RgGQzfAQAA","CodeDeliveryDetails":
{"DeliveryMedium":"SMS","Destination":"+*******8869"}}
Some Error
Optional(Error Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=-1000 "startMultiFactorAuthentication not implemented by authentication delegate" UserInfo={NSLocalizedDescription=startMultiFactorAuthentication not implemented by authentication delegate})
Lorsque j'ai fourni un mot de passe incorrect, le corps de la réponse est
Response body:
{"__type":"NotAuthorizedException","message":"Incorrect username or password."}
Some Error
Optional(Error Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=12 "(null)" UserInfo={__type=NotAuthorizedException, message=Incorrect username or password.})
Pourriez-vous suggérer ce que je fais mal ici?
J'ai également suivi les mêmes étapes mentionnées dans la question des affiches d'origine, mais l'application ne bascule jamais vers l'écran de connexion au démarrage. J'ai vérifié que le code permettant de changer de vue est correct en plaçant directement dans la méthode AppDelegate.application (....). Il semble que la méthode de suppression startPasswordAuthentication (...) ne soit jamais appelée. Quelqu'un peut-il publier un lien vers un exemple d'application qui passe à l'écran de connexion à l'aide du protocole AWSCognitoIdentityInteractiveAuthenticationDelegate?
Il semble que la méthode "startMultiFactorAuthentication" n’est pas implémentée dans le délégué, c’est pourquoi un mot de passe incorrect est détecté, mais lorsque le mot de passe corrigé est donné, il est transféré à MFA, mais la fonction de démarrage MFA n’est pas trouvée dans le délégué, d'où la connexion. échoue.
J'ai suivi les mêmes étapes mentionnées précédemment en utilisant Swift et j'ai remarqué que la variable didCompletePasswordAuthenticationStepWithError
n'est jamais appelée, bien que LogInViewController
soit étendu à AWSCognitoIdentityPasswordAuthentication
.
De même, startPasswordAuthentication()
n'est pas appelé dans le délégué même si ce dernier implémente également AWSCognitoIdentityInteractiveAuthenticationDelegate
.
Je me demande si cela pose un problème avec l'implémentation de Swift, étant donné que l'exemple Objective-C fourni par Amazon fonctionne correctement.
Voici comment je l'ai fait dans Swift, avec un seul ViewController et sans rien configurer dans AppDelegate:
class LoginViewController: UIViewController, AWSCognitoIdentityInteractiveAuthenticationDelegate, AWSCognitoIdentityPasswordAuthentication {
var passwordAuthenticationCompletion = AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>()
var pool = AWSCognitoIdentityUserPool.init(forKey: "UserPool")
override func viewDidLoad() {
super.viewDidLoad()
//setup service configuration
let serviceConfiguration = AWSServiceConfiguration.init(region: .USEast1, credentialsProvider: nil)
//create and config a pool
let configuration = AWSCognitoIdentityUserPoolConfiguration.init(clientId: "YourClientId", clientSecret: "YourClientId", poolId: "YourPoolId")
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: configuration, forKey: "UserPool")
pool = AWSCognitoIdentityUserPool.init(forKey: "UserPool")
pool.delegate = self
}
@IBAction func logInButtonPressed(sender: UIButton) {
pool.getUser().getDetails()
}
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
return self
}
func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
let result = AWSCognitoIdentityPasswordAuthenticationDetails.init(username: "username", password: "password")
}
}
Pour moi, je continuais à avoir cette erreur parce que la vérification était en cours, mais je n'ai PAS vérifié l'utilisateur avant de tenter de me connecter. Une fois vérifié, tout a fonctionné.
Je pense qu'il y a un problème dans ceci:
Objet-c ->
self.passwordAuthenticationCompletion.result = [[AWSCognitoIdentityPasswordAuthenticationDetails alloc] initWithUsername: username password:password];
Swift -> self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails.init(username: username, password: password))
et parce qu'il y a quelque chose qui cloche dans la déclaration précédente, la méthode "didCompletePasswordAuthenticationStepWithError" n'a pas été déclenchée.
J'ai essayé beaucoup de choses sans chance: ( - J'ai essayé de tout mettre en œuvre dans Swift (non au travail) - J'ai essayé d'ajouter les fichiers Object-C à mon projet basé sur Swift (non au travail)
Donc, je pense que je vais utiliser l'échantillon original comme départ pour mon projet.
Mettre à jour:
J'implémente SignInViewController & MFAController dans Swift et importe ces fichiers Swift dans un projet Object-C. et cela fonctionne bien!. Donc, maintenant, je suis sûr qu’il ya un problème ou un bogue lorsque nous essayons d’implémenter les protocoles "AWSCognitoIdentityPasswordAuthentication" && "AWSCognitoIdentityMultiFactorAuthentication" dans un projet basé sur Swift. La seule solution que j'ai trouvée est d'aller avec un projet basé sur Object-c.
Je suis le même exemple et traite le même problème, et je n'ai pas complètement réussi à le résoudre, mais je soupçonne fortement que le problème a quelque chose à voir avec cette fonction qui ne s'exécute jamais:
func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
dispatch_async(dispatch_get_main_queue(), {() -> Void in
if self.usernameText == nil{
self.usernameText = authenticationInput.lastKnownUsername
}
})
}
J'ai essayé de mettre des points d'arrêt et des déclarations d'impression avec cette méthode et elle ne semble jamais être activée. Je suggérerais de faire la même chose dans votre code, car il semblerait que votre problème soit identique au mien. J'ai regardé dans l'exemple et je suis incapable de trouver un endroit où la méthode est appelée manuellement. J'ai remarqué que dans votre code, vous intialize la valeur de passwordAuthenticationCompletion
comme ceci:
var passwordAuthenticationCompletion = AWSTaskCompletionSource()
Il semble que getPasswordAuthenticationDetails()
soit censé être appelé avant que cette ligne de la méthode de connexion utilise la valeur appropriée: self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails(username: emailTextField.text, password: passwordTextField.text))
Cela fait un moment que je suis coincé pour essayer d'aller plus loin, mais je pense toujours que c'est le bon moyen d'implémenter l'enregistrement/la connexion de l'utilisateur. Il est possible qu'une partie du code de l'exemple ne se traduise pas proprement en Swift. Par conséquent, certaines fonctions importantes ne sont pas déclenchées. Je vais continuer à chercher et mettre à jour ma réponse si je confirme une solution.