I have built authentication into my app using Firebase/Facebook Auth and now I would like to build into my app a system for managing a user's presence (eg; updating a 'status' field on Firebase for the user to say 'online or offline') as described in the Firebase Docs here
*My question is... where do I put all this managing presense code?, here is my 'signIntoFirebase' function. would I create a separate function called 'manageUserPresense' and call it straight after 'firebaseLoginComplete'?
func signIntoFirebase(firebaseLoginComplete: #escaping (_ status: Bool, _ error: Error?) -> ()){
//Getting FB authentication
guard let authenticationToken = AccessToken.current?.authenticationToken else { return }
//Getting credential using authentication token
let credential = FacebookAuthProvider.credential(withAccessToken: authenticationToken)
//Signing in to Firebase using FB authentication token
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error { //if there is an error
print (error)
firebaseLoginComplete(false, error)
return
}
firebaseLoginComplete(true, nil)
self.isLoggedIn = true
print ("Successfully authenticated into Firebase")
}
}//end func
Related
I am using the swift package AWSiOSSDKV2 from https://github.com/aws-amplify/aws-sdk-ios-spm
I can log in using the SDK but how can I use refresh Token to generate new accessToken using their SDK?
func login() {
let pool = AWSCognitoIdentityUserPool(forKey: PortalUserConfig.POOL_NAME)
if let user = pool?.getUser() {
user.getSession(username, password: password, validationData: nil).continueWith(block: { task -> Any? in
if let error = task.error {
let errorMessage = (error as NSError).userInfo["message"] as? String
} else {
guard let accessToken = task.result?.accessToken else {
return nil
}
print(accessToken)
let refreshToken = task.result?.refreshToken
print(refreshToken)
}
return nil
})
}
}
The Amplify library will automatically fetch new id and access tokens if they have expired and the refresh token is still valid. You can adjust the lifetime of your refresh tokens in the settings for your User Pool client. To learn more about OAuth token lifetime strategies, you can read up on the options over at OAuth.com.
If i understood correct the user UID its this is a unique uid, until the user logs out. I mean he can close/open the app many times and user UID must be the same.
I have test class:
class UserFirebase {
func authorization(completionHandler: #escaping (Result<AuthDataResult?, Error>) -> Void) {
Auth.auth().signInAnonymously { authResult, error in
if error == nil {
completionHandler(.success(authResult))
return
}
completionHandler(.failure(error))
}
}
func singOut() {
try? Auth.auth().signOut()
}
func getUserUUID() -> String? {
return Auth.auth().currentUser?.uid
}
func isAuthorized() -> Bool {
return Auth.auth().currentUser != nil
}
}
when app is running i using this class like this:
let userFirebaseManager: UserFirebase = UserFirebase()
if userFirebaseManager.isAuthorized() {
// make something
} else {
userFirebaseManager.authorization(completionHandler: {[weak self] (result) in
// make something
})
}
every time I start the app, the user isAuthorized() == false. Shouldn't it persist until I press logout?
UPD:
why does my currentUser.uid change every time I restart the application?
The UID of an anonymous user is deleted when the user signs out. It cannot be regained after that, not even when you sign in on the same device.
Also see:
How constant is the Firebase Anonymous ID
Firebase Anonymous Authentication
Firebase Anonymous Auth: Restore account
How to logout and then re authenticate an anonymous firebase user
Anonymous users are signed out once the app is closed. I'm not sure why you need to sign in anonymously AND keep that UUID known. If you need to sign in anonymously just to securely access your Firebase project, it shouldn't matter if the user gets signed out. If you want to preserve the UUID, the user needs to create an account with Auth.auth().createUser(withEmail: String, password: String, completion: ((AuthDataResult?, Error?) -> Void)
I am able to create an account and login via Twitter; however, with each login attempt the user is taken to a Twitter web page where they have to login with their credentials. Given this happens every time, it negates the value of this kind of social login. I have Facebook and Google logins working without this issue on Firebase. Below is the code used to login:
private func loginWithTwitter() {
TWTRTwitter.sharedInstance().logIn { (session, error) in
guard error == nil && session != nil else {
self.spinner.dismiss()
Utilities.displayAlert(title: "Login Error", msg: error?.localizedDescription ?? "Could not login with Twitter at this time.", controller: self)
return
}
let credential = TwitterAuthProvider.credential(withToken: session!.authToken, secret: session!.authTokenSecret)
Auth.auth().signInAndRetrieveData(with: credential, completion: { (result, error) in
if let error = error {
self.spinner.dismiss()
self.present(self.sharedManager.getAlertWith(title: "Login Error", andMsg: error.localizedDescription), animated: true)
return
}
self.loadUserInfo()
})
}
}
I should also add that I do have the use for logins box checked on the Twitter Developers page.
UPDATE:
Based on the post of Pratik below and here is a modified version that does seem to work. However, I am not sure if it's the right long-term answer. I wonder about token expiration for example. Thoughts on if there is a better way than this?
private func loginWithTwitter() {
let token = TWTRTwitter.sharedInstance().sessionStore.session()?.authToken
let secret = TWTRTwitter.sharedInstance().sessionStore.session()?.authTokenSecret
guard token != nil && secret != nil else {
self.present(self.sharedManager.getAlertWith(title: "Login Error", andMsg: "Could not login with Twitter. Please try again."), animated: true)
return
}
let credential = TwitterAuthProvider.credential(withToken: token!, secret: secret!)
Auth.auth().signInAndRetrieveData(with: credential, completion: { (result, error) in
if let error = error {
self.spinner.dismiss()
self.present(self.sharedManager.getAlertWith(title: "Login Error", andMsg: error.localizedDescription), animated: true)
return
}
self.loadUserInfo()
})
}
I'm not sure with will help or not, but im using twitter login in my app and once i authenticate with twitter i used below function to when user open app again for validation.
func silentLoginWithTwitter()
{
let client = TWTRAPIClient.withCurrentUser()
let request = client.urlRequest(withMethod: "GET", urlString: "https://api.twitter.com/1.1/account/verify_credentials.json", parameters: ["include_email": "true", "skip_status": "true"], error: nil)
client.sendTwitterRequest(request, completion: { (response:URLResponse?, data:Data?, error:Error?) in
if error == nil
{
// Open main page of application
}else
{
print("Error: Twitter : \(String(describing: error))")
// Open Login page again
}
})
}
OR you can check session auth
if let authSession = TWTRTwitter.sharedInstance().sessionStore.session()?.authToken {
TWTRTwitter.sharedInstance().sessionStore.isValidOauthToken(authSession)
}
Hope this will help
I have the structure set up for updating user attributes, in this case the preferred username to use as an alias for signing in.
var attributes = [AWSCognitoIdentityUserAttributeType]()
let prefUsername = AWSCognitoIdentityUserAttributeType();
prefUsername?.name = "preferred_username";
prefUsername?.value = usernameField.text!;
attributes.append(prefUsername!);
let attributesRequest = AWSCognitoIdentityProviderUpdateUserAttributesRequest();
attributesRequest.userAttributes = attributes;
idProvider?.updateUserAttributes(attributesRequest)
Only thing I have no idea how to do is get the access token. I've looked in as much documentation as I could think of but I had no luck finding place to get access token.
You can use the api to initiate auth and get an AccessToken from the AuthenticationResult.
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
/// Function to retreive the current token for the logged in user.
///
/// - Parameter completion: A completion block with an error or the token. Called back on the main thread.
public func getJWT(completion: #escaping((_ error: Error?, _ token: AWSCognitoIdentityUserSessionToken?) -> Void)) {
guard let user = self.pool.currentUser() else {
let nsError = NSError(domain: "JWT Error", code: 500, userInfo: ["message": "No Logged in user"])
completion(nsError, nil)
return
}
user.getSession().continueWith { (task) -> Any? in
DispatchQueue.main.async {
if let error = task.error {
completion(error, nil)
}else if let token = task.result?.idToken {
completion(nil, token)
}else {
completion(nil, nil)
}
}
}
}
Where self.pool is the AWSCognitoIdentityUserPool you hopefully set up correctly.
You would have to authenticate first to establish a session with Cognito User Pools. That session would contain an access token which you can then pass to every subsequent request. I see you are using the low level SDK methods. Here is a sample in swift for SignIn:
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift
I've set up Firebase authentication for my iOS app using Facebook, Google & email/password sign in and it's all working fine. This authentication only happens when the user wants to access high-priority parts of my app (i.e. I don't require users to sign in to start using the app).
On app start up, I sign users in anonymously in the background and that's working fine too.
I've read the documentation but I'm struggling to understand the code required to enable me to link an anonymous account to a Facebook/email signed in account in the following flow:
new user opens app
user signed in anonymously in the background (new user.uid "A" created)
low priority data stored against anonymous user in Firebase realtime DB
user hits a high-priority area so needs to authenticate
user signs in using Facebook (new user.uid "B" created)
previous user.uid "A" needs to be linked to user.uid "B"
My method currently looks like this:
func signupWithFacebook(){
// track the anonymous user to link later
let prevUser = FIRAuth.auth()?.currentUser
FBSDKLoginManager().logInWithReadPermissions(["public_profile", "email"], fromViewController: self) { (result, error) in
if let token = result?.token?.tokenString {
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(token)
FIRAuth.auth()?.signInWithCredential(credential, completion: { (user, error) in
if user != nil && error == nil {
// Success
self.success?(user: user!)
dispatch_async(dispatch_get_main_queue(), {
self.dismissViewControllerAnimated(true, completion: nil)
})
}
})
}
}
}
Any pointers to remove the confusion would be great.
UPDATE:
I've realised I was confused about the app logic because of users being created during testing. Instead of 2 separate users being created for the above scenario (one authenticated via Facebook and another anonymously), all that happens is that the original anonymous user.uid "A" is "linked" to some Facebook authentication credentials. In the Firebase console this is shown by the anonymous uid changing from anonymous to one with the Facebook logo next to it.
This is what my working method looks like:
func signupWithFacebook(){
FBSDKLoginManager().logInWithReadPermissions(["public_profile", "email"], fromViewController: self) { (result, error) in
if let token = result?.token?.tokenString {
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(token)
FIRAuth.auth()?.currentUser!.linkWithCredential(credential) { (user, error) in
if user != nil && error == nil {
// Success
self.success?(user: user!)
dispatch_async(dispatch_get_main_queue(), {
self.dismissViewControllerAnimated(true, completion: nil)
})
} else {
print("linkWithCredential error:", error)
}
}
}
}
}
So your code follows the first 2 steps in this link. But the documentation explicity says not to call signInWithCredential but instead call
FIRAuth.auth()?.currentUser.linkWithCredential(credential) { (user, error) in
// ...
}
After getting your credential from Facebook's SDK.
Quote from link: "If the call to linkWithCredential:completion: succeeds, the user's new account can access the anonymous account's Firebase data."