How to get username from AWS Cognito - Swift - swift

Q & A Style: See Answer Below
How Can I get the username from a user logged in with Cognito?
I've done this and my user is logged in, now what?
AWSAuthUIViewController.presentViewController(
with: self.navigationController!,
configuration: config, completionHandler: { (provider: AWSSignInProvider, error: Error?) in
if error == nil {
//get parameters
}
} else {
print(error as Any)
}
})
}

Prerequisites:
App registered with MobileHub
Cognito Setup in MobileHub
Mobilehub integrated with Swift Project using AWS SDK
If you're like me, you did this with little to no difficulty and now you're stuck trying to get the username and other parameters from the logged in user. There are a lot of answers, but thus far, I haven't stumbled upon one that gets you all the way there.
I was able to piece this together from various sources:
func getUsername() {
//to check if user is logged in with Cognito... not sure if this is necessary
let identityManager = AWSIdentityManager.default()
let identityProvider = identityManager.credentialsProvider.identityProvider.identityProviderName
if identityProvider == "cognito-identity.amazonaws.com" {
print("************LOGGED IN WITH COGNITO************")
let serviceConfiguration = AWSServiceConfiguration(region: .USWest2, credentialsProvider: nil)
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "YourClientID", clientSecret: "YourSecretKey", poolId: "YourPoolID")
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: userPoolConfiguration, forKey: "YourPoolName (typically formatted as YourAppName_userpoool_MOBILEHUB_12345678")
let pool = AWSCognitoIdentityUserPool(forKey: "YourPoolName")
// the following line doesn't seem to be necessary and isn't used so I've commented it out, but it is included in official documentation
// let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USWest2, identityPoolId: "YourPoolID", identityProviderManager:pool)
if let username = pool.currentUser()?.username {
print("Username Retrieved Successfully: \(username)")
} else {
print("Error getting username from current user - attempt to get user")
let user = pool.getUser()
let username = user.username
print("Username: \(username)")
}
}
}
To get your ClientID, Secret Key, and PoolID, check your awsconfiguration.json
To get your PoolName, login to MobileHub, and in your project's backend, go to User Sign in, click Email and Password, then click Edit in Cognito. The following page will have your Pool Name as "YourAppName_userpool_MOBILEHUB_12345678"
Edit: To get all of the attributes as well:
if let userFromPool = pool.currentUser() {
userFromPool.getDetails().continueOnSuccessWith(block: { (task) -> Any? in
DispatchQueue.main.async {
if let error = task.error as NSError? {
print("Error getting user attributes from Cognito: \(error)")
} else {
let response = task.result
if let userAttributes = response?.userAttributes {
print("user attributes found: \(userAttributes)")
for attribute in userAttributes {
if attribute.name == "email" {
if let email = attribute.value {
print("User Email: \(email)")
}
}
}

If you're using Cognito User Pools, you can use this:
import AWSUserPoolsSignIn
AWSCognitoUserPoolsSignInProvider.sharedInstance()
.getUserPool()
.currentUser()?
.username

Related

MSAL integration B2C AD in Swift Xcode Getting Error after hitting Authorize "Could not acquire token

There are some similar question however none of those solve my problem.
Using Xcode 11.1
MacOS Cataline 10.15
I clone this "active-directory-b2c-ios-swift-native-msal" and try to run getting Error "Could not acquire token: Error Domain=MSALErrorDomain Code=-50000 "(null)" UserInfo={MSALErrorDescriptionKey=Failed to start an interactive session, MSALInternalErrorCodeKey=-42008, MSALCorrelationIDKey=C9207A45-6A7D-416B-90E4-93E08F28A637}"
After changing B2C details same issue getting .
Please let me know what is issue, Is this issue for Xcode/OS/MSAL version or some issue with Code??
I tried with default configuration mention in git repo "active-directory-b2c-ios-swift-native-msal" and also tried after below changed let kTenantName = "dovervsg.onmicrosoft.com" // Your tenant name
let kClientID = "xxxxxxxxxxxxxxxxxxxxxxx" // Your client ID from the portal when you created your application
let kSignupOrSigninPolicy = "B2C_1-policy" // Your signup and sign-in policy you created in the portal
let kEditProfilePolicy = "b2c_1_edit_profile" // Your edit policy you created in the portal
let kResetPasswordPolicy = "B2C_1_reset_password" // Your reset password policy you created in the portal
let kGraphURI = "https://dev-vsg.dovertech.co.in" // This is your backend API that you've configured to accept your app's tokens
let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/User.Read"] // This is a scope that you've configured your backend API to look for.
// tried with this scope format as well, let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/api/User.Read"] // This is a scope that you've configured your backend API to look for.
let kTenantName = "dovervsg.onmicrosoft.com" // Your tenant name
let kClientID = "xxxxxxxxxxxxxxxxxxxxxxx" // Your client ID from the portal when you created your application
let kSignupOrSigninPolicy = "B2C_1-policy" // Your signup and sign-in policy you created in the portal
let kEditProfilePolicy = "b2c_1_edit_profile" // Your edit policy you created in the portal
let kResetPasswordPolicy = "B2C_1_reset_password" // Your reset password policy you created in the portal
let kGraphURI = "https://dev-vsg.dovertech.co.in" // This is your backend API that you've configured to accept your app's tokens
let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/User.Read"] // This is a scope that you've configured your backend API to look for.
// tried with this scope format as well, let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/api/User.Read"] // This is a scope that you've configured your backend API to look for
// DO NOT CHANGE - This is the format of OIDC Token and Authorization endpoints for Azure AD B2C.
let kEndpoint = "https://login.microsoftonline.com/tfp/%#/%#"
var application: MSALPublicClientApplication!
var accessToken: String?
#IBOutlet weak var loggingText: UITextView!
#IBOutlet weak var signoutButton: UIButton!
#IBOutlet weak var callGraphApiButton: UIButton!
#IBOutlet weak var editProfileButton: UIButton!
#IBOutlet weak var refreshTokenButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
//super.viewDidLoad()
do {
/**
Initialize a MSALPublicClientApplication with a MSALPublicClientApplicationConfig.
MSALPublicClientApplicationConfig can be initialized with client id, redirect uri and authority.
Redirect uri will be constucted automatically in the form of "msal<your-client-id-here>://auth" if not provided.
The scheme part, i.e. "msal<your-client-id-here>", needs to be registered in the info.plist of the project
*/
let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
// Provide configuration for MSALPublicClientApplication
// MSAL will use default redirect uri when you provide nil
let pcaConfig = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: nil, authority: authority)
self.application = try MSALPublicClientApplication(configuration: pcaConfig)
} catch {
self.updateLoggingText(text: "Unable to create application \(error)")
}
}
/**
This button will invoke the authorization flow and send the policy specified to the B2C server.
Here we are using the `kSignupOrSignInPolicy` to sign the user in to the app. We will store this
accessToken for subsequent calls.
*/
#IBAction func authorizationButton(_ sender: UIButton) {
do {
/**
authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
/**
Acquire a token for a new account using interactive authentication
- scopes: Permissions you want included in the access token received
in the result in the completionBlock. Not all scopes are
gauranteed to be included in the access token returned.
- completionBlock: The completion block that will be called when the authentication
flow completes, or encounters an error.
*/
let webViewParameters = MSALWebviewParameters(parentViewController: self)
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
parameters.promptType = .selectAccount
print( parameters.promptType = .selectAccount)
parameters.authority = authority
debugPrint( parameters.authority = authority)
application.acquireToken(with: parameters) { (result, error) in
guard let result = result else {
self.updateLoggingText(text: "Could not acquire token: \(error ?? "No error informarion" as! Error)")
return
}
self.accessToken = result.accessToken
self.updateLoggingText(text: "Access token is \(self.accessToken ?? "Empty")")
self.signoutButton.isEnabled = true
self.callGraphApiButton.isEnabled = true
self.editProfileButton.isEnabled = true
self.refreshTokenButton.isEnabled = true
}
} catch {
self.updateLoggingText(text: "Unable to create authority \(error)")
}
}
#IBAction func editProfile(_ sender: UIButton) {
do {
/**
authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
let authority = try self.getAuthority(forPolicy: self.kEditProfilePolicy)
/**
Acquire a token for a new account using interactive authentication
- scopes: Permissions you want included in the access token received
in the result in the completionBlock. Not all scopes are
gauranteed to be included in the access token returned.
- completionBlock: The completion block that will be called when the authentication
flow completes, or encounters an error.
*/
let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kEditProfilePolicy)
let webViewParameters = MSALWebviewParameters(parentViewController: self)
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
parameters.authority = authority
parameters.account = thisAccount
application.acquireToken(with: parameters) { (result, error) in
if let error = error {
self.updateLoggingText(text: "Could not edit profile: \(error)")
} else {
self.updateLoggingText(text: "Successfully edited profile")
}
}
} catch {
self.updateLoggingText(text: "Unable to construct parameters before calling acquire token \(error)")
}
}
#IBAction func refreshToken(_ sender: UIButton) {
do {
/**
authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
/**
Acquire a token for an existing account silently
- scopes: Permissions you want included in the access token received
in the result in the completionBlock. Not all scopes are
gauranteed to be included in the access token returned.
- account: An account object that we retrieved from the application object before that the
authentication flow will be locked down to.
- completionBlock: The completion block that will be called when the authentication
flow completes, or encounters an error.
*/
guard let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kSignupOrSigninPolicy) else {
self.updateLoggingText(text: "There is no account available!")
return
}
let parameters = MSALSilentTokenParameters(scopes: kScopes, account:thisAccount)
parameters.authority = authority
self.application.acquireTokenSilent(with: parameters) { (result, error) in
if let error = error {
let nsError = error as NSError
// interactionRequired means we need to ask the user to sign-in. This usually happens
// when the user's Refresh Token is expired or if the user has changed their password
// among other possible reasons.
if (nsError.domain == MSALErrorDomain) {
if (nsError.code == MSALError.interactionRequired.rawValue) {
// Notice we supply the account here. This ensures we acquire token for the same account
// as we originally authenticated.
let webviewParameters = MSALWebviewParameters(parentViewController: self)
let parameters = MSALInteractiveTokenParameters(scopes: self.kScopes, webviewParameters: webviewParameters)
parameters.account = thisAccount
self.application.acquireToken(with: parameters) { (result, error) in
guard let result = result else {
self.updateLoggingText(text: "Could not acquire new token: \(error ?? "No error informarion" as! Error)")
return
}
self.accessToken = result.accessToken
self.updateLoggingText(text: "Access token is \(self.accessToken ?? "empty")")
}
return
}
}
self.updateLoggingText(text: "Could not acquire token: \(error)")
return
}
guard let result = result else {
self.updateLoggingText(text: "Could not acquire token: No result returned")
return
}
self.accessToken = result.accessToken
self.updateLoggingText(text: "Refreshing token silently")
self.updateLoggingText(text: "Refreshed access token is \(self.accessToken ?? "empty")")
}
} catch {
self.updateLoggingText(text: "Unable to construct parameters before calling acquire token \(error)")
}
}
#IBAction func callApi(_ sender: UIButton) {
guard let accessToken = self.accessToken else {
self.updateLoggingText(text: "Operation failed because could not find an access token!")
return
}
let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 30
let url = URL(string: self.kGraphURI)
var request = URLRequest(url: url!)
request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
let urlSession = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
self.updateLoggingText(text: "Calling the API....")
urlSession.dataTask(with: request) { data, response, error in
guard let validData = data else {
self.updateLoggingText(text: "Could not call API: \(error ?? "No error informarion" as! Error)")
return
}
let result = try? JSONSerialization.jsonObject(with: validData, options: [])
guard let validResult = result as? [String: Any] else {
self.updateLoggingText(text: "Nothing returned from API")
return
}
self.updateLoggingText(text: "API response: \(validResult.debugDescription)")
}.resume()
}
#IBAction func signoutButton(_ sender: UIButton) {
do {
/**
Removes all tokens from the cache for this application for the provided account
- account: The account to remove from the cache
*/
let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kSignupOrSigninPolicy)
if let accountToRemove = thisAccount {
try application.remove(accountToRemove)
} else {
self.updateLoggingText(text: "There is no account to signing out!")
}
self.signoutButton.isEnabled = false
self.callGraphApiButton.isEnabled = false
self.editProfileButton.isEnabled = false
self.refreshTokenButton.isEnabled = false
self.updateLoggingText(text: "Signed out")
} catch {
self.updateLoggingText(text: "Received error signing out: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
if self.accessToken == nil {
signoutButton.isEnabled = false
callGraphApiButton.isEnabled = false
editProfileButton.isEnabled = false
refreshTokenButton.isEnabled = false
}
}
func getAccountByPolicy (withAccounts accounts: [MSALAccount], policy: String) throws -> MSALAccount? {
for account in accounts {
// This is a single account sample, so we only check the suffic part of the object id,
// where object id is in the form of <object id>-<policy>.
// For multi-account apps, the whole object id needs to be checked.
if let homeAccountId = account.homeAccountId, let objectId = homeAccountId.objectId {
if objectId.hasSuffix(policy.lowercased()) {
return account
}
}
}
return nil
}
/**
The way B2C knows what actions to perform for the user of the app is through the use of `Authority URL`.
It is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
identifier within the directory itself (e.g. a domain associated to the
tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
use for the current user flow.
*/
func getAuthority(forPolicy policy: String) throws -> MSALB2CAuthority {
guard let authorityURL = URL(string: String(format: self.kEndpoint, self.kTenantName, policy)) else {
throw NSError(domain: "SomeDomain",
code: 1,
userInfo: ["errorDescription": "Unable to create authority URL!"])
}
return try MSALB2CAuthority(url: authorityURL)
}
func updateLoggingText(text: String) {
DispatchQueue.main.async{
self.loggingText.text = text
}
}
}
After run getting error above
The sample has been updated and should be working as expected now. It is now updated to handle *.b2clogin.com and now adds sui_si and edit profile to the known authorities list.
let siginPolicyAuthority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
let editProfileAuthority = try self.getAuthority(forPolicy: self.kEditProfilePolicy)
// Provide configuration for MSALPublicClientApplication
// MSAL will use default redirect uri when you provide nil
let pcaConfig = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: nil, authority: siginPolicyAuthority)
pcaConfig.knownAuthorities = [siginPolicyAuthority, editProfileAuthority]
self.application = try MSALPublicClientApplication(configuration: pcaConfig)
and
func getAuthority(forPolicy policy: String) throws -> MSALB2CAuthority {
guard let authorityURL = URL(string: String(format: self.kEndpoint, self.kAuthorityHostName, self.kTenantName, policy)) else {
throw NSError(domain: "SomeDomain",
code: 1,
userInfo: ["errorDescription": "Unable to create authority URL!"])
}
return try MSALB2CAuthority(url: authorityURL)
}

How do I grab credential values for re authentication? (Question updated)

I am trying to allow users to change their email or password. I did some research on how re authentication work, most of the questions I had were answered however when creating the credential how do I grab the users email / password in the .credentialWithEmail(email, password: password) section? I'm not sure what to enter in those fields.
When I take a look at the quick help tab, it explains the Parameters:
email
The user's email address.
password
The user's password.
Here is the code
let credential = FIREmailPasswordAuthProvider.credentialWithEmail(email, password: password)
func updateEmail() {
guard let updatedEmail = self.updatedEmail else { return }
let user = Auth.auth().currentUser
guard let currentUid = Auth.auth().currentUser?.uid else { return }
// re authenticate the user
user?.reauthenticate(with: credential, completion: { (result, error) in
if let error = error {
// An error happened.
print(error._code)
self.handleError(error)
} else {
guard self.emailChanged == true else { return }
user?.updateEmail(to: self.emailTextField.text!, completion: { (error) in
if let error = error {
self.handleError(error)
} else {
print("Email Change Success")
USER_REF.child(currentUid).child("email").setValue(updatedEmail) { (err, ref) in
self.dismiss(animated: true, completion: nil)
}
}
}
)}
}
)}
If you are asking why you can't get the email or password after you set FIREmailPasswordAuthProvider, it is because it is set only. There is no function to retrieve email/password after you set FIREmailPasswordAuthProvider. Firebase does not have a way to retrieve set passwords from their Auth class. The user email can be retrieved with let userEmail = Auth.auth().currentUser?.email
You may need to redesign you code to allow the app to save passwords locally using NSUserDefaults or simply make the user reenter them.

Having trouble with query-based Realms

Sorry, my subject isn't very specific.
I'm dealing with managing multiple users in my realm and having some problems. I have a user register with their email and name, then when realm logs in and creates a SyncUser, I create a new YpbUser object with the email, name, and SyncUser.current.identity in YpbUser.id.
When a user logs in, I want to use their email to look up whether there's an existing YpbUser for their email. I'm doing it this way so in the future if someone uses username/password and then uses Google auth (not yet implemented), those SyncUsers can get to the same YpbUser.
Below is my code for checking for an existing YpbUser, along with my realm setup code. The problem I'm having is that it usually, but not always, fails to find a YpbUser even if that user exists (I'm looking at the YpbUser list in realm studio and the user with that email is definitely there!)
Inspecting from within existingUser(for:in:), users is usually 0, but it sometimes non-zero.
I assume the issue lies somewhere in the fact that I'm pretty much just guessing on how to use SyncSubscription.observe.
Please help?
fileprivate func openRealmWithUser(user: SyncUser) {
DispatchQueue.main.async { [weak self] in
let config = user.configuration(realmURL: RealmConstants.realmURL, fullSynchronization: false, enableSSLValidation: true, urlPrefix: nil)
self?.realm = try! Realm(configuration: config)
let songSub = self?.realm?.objects(Song.self).subscribe()
let usersSub = self?.realm?.objects(YpbUser.self).subscribe()
self?.usersToken = usersSub?.observe(\.state, options: .initial) { state in
if !(self?.proposedUser.email.isEmpty)! {
self?.findYpbUser(in: (self?.realm)!)
}
}
self?.performSegue(withIdentifier: Storyboard.LoginToNewRequestSegue, sender: nil)
}
}
fileprivate func findYpbUser(in realm: Realm) {
if proposedUser != YpbUser() { // self.proposedUser.email gets set from the login method
let existingUser = YpbUser.existingUser(for: proposedUser, in: realm)
guard existingUser == nil else { // If we find the YpbUser, set as current:
try! realm.write {
pr("YpbUser found: \(existingUser!)")
YpbUser.current = existingUser }
return
}
pr("YpbUser not found.") // i.e., SyncUser set, but no YpbUser found. i.e., creating a new YpbUser
createNewYpbUser(for: proposedUser, in: realm)
}
}
extension YpbUser {
class func existingUser (for proposedUser: YpbUser, in realm: Realm) -> YpbUser? {
let users = realm.objects(YpbUser.self)
let usersWithThisEmail = users.filter("email = %#", proposedUser.email)
if let emailUser = usersWithThisEmail.first {
return emailUser
}
return nil
}
}

Setting IAM role for Lambda after login with Cognito on iOS

I'm getting a trouble when trying to connect AWSCognito to AWSLambda to pass an Auth role into it. My application has serverless architecture based on CognitoUserPool's, Lambda and IAM. So I have one configuration for all these things like the following:
let credentialsProvider = AWSCognitoCredentialsProvider(
regionType: CognitoIdentityUserPoolRegion,
identityPoolId: CognitoIdentityUserPoolIdentityId,
identityProviderManager: nil
)
// setup service configuration
let serviceConfiguration = AWSServiceConfiguration(
region: CognitoIdentityUserPoolRegion,
credentialsProvider: credentialsProvider
)
// create pool configuration
let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: CognitoIdentityUserPoolAppClientId,
clientSecret: nil,
poolId: CognitoIdentityUserPoolId)
// initialize user pool client
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: AWSCognitoUserPoolsSignInProviderKey)
// fetch the user pool client we initialized in above step
let pool = AWSCognitoIdentityUserPool(forKey: AWSCognitoUserPoolsSignInProviderKey)
pool.delegate = self
AWSLambdaInvoker.register(with: serviceConfiguration!, forKey: AWSCognitoUserPoolsSignInProviderKey)
Then I do log in using the following snippet:
func signIn(username: String, password: String) -> Promise<Bool> {
let user = AWSCognitoIdentityUserPool(forKey: AWSCognitoUserPoolsSignInProviderKey).getUser()
return Promise(resolvers: { resolve, reject in
user.getSession(username, password: password, validationData: nil)
.continueWith(block: { task -> Any? in
if let error = task.error {
let message = (error as NSError).userInfo["message"] as? String
return reject(AWSError.SignIn.sessionError(message))
}
guard let idToken = task.result?.idToken?.tokenString else {
return reject(AWSError.SignIn.noTokenProvided)
}
guard let identityId = user.username else {
return reject(AWSError.SignIn.noIdentityIdProvided)
}
return resolve(true)
})
})
}
The issue is that I'm always getting an error saying that:
User:
arn:aws:sts:{identity}:assumed-role/Cognito_myUnauth_Role/CognitoIdentityCredentials
is not authorized to perform: InvokeLambda
I tested an official example, I wrote my own implementations, and also I tried all the solutions mentioned in this thread: https://github.com/aws/aws-sdk-ios/issues/357 , but there was no luck with it.
I also tried implementing my own AWSCognitoCredentialsProviderHelper but the error is still presented. Do you have any ideas or maybe you also have faced with this issue so you can help me.
Thanks

Attempting to save username from twitter user to Firebase database iOS app

I'm attempting to save a twitter users username into the database for later reference my code below is executing but doesn't seem to be accessing the database or saving the username into the database and I'm really lost as to why. I'm attempting to have the username and userID so I can retrieve information about the user for a profile page in the app. So if I can avoid saving this data to the database all together that works too but I don't think it can be done that way.
fileprivate func setupTwitterButton() {
let twitterButton = TWTRLogInButton { (session, error) in
if let err = error {
print("Failed to login via Twitter: ", err)
return
}
// debug statement
//print("Successfully logged in using Twitter")
HUD.show(.labeledProgress(title: nil, subtitle: "Signing In"))
//we've authenticated twitter, time to log into firebase
guard let token = session?.authToken else { return }
guard let secret = session?.authTokenSecret else { return }
let creds = FIRTwitterAuthProvider.credential(withToken: token, secret: secret)
let dbref = FIRDatabase.database().reference()
let usersref = dbref.child("users")
let uid = session?.userID
//let user = FIRAuth.auth?.signIn
print("Creating user")
let newUserReference = usersref.child(uid!)
newUserReference.setValue(["username": session?.userName])
Okay so after some debugging it was pretty simple where I went wrong. I was trying to write to the database before I'd authenticated with the database. Once I had put my code for writing to the database after I'd authenticated it all worked correctly.