iOS7 Access user Facebook Profile Picture - facebook

So i'm trying to get a users Facebook profile image using SLRequest. I feel like I've scoured the entire internet to no avail and am at my wits end. Here's the dilemma...
Version 1 of the code:
let store = ACAccountStore()
let type = store.accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierFacebook)
store.requestAccessToAccountsWithType(type, options: [ ACFacebookAppIdKey: "1437725166510606", ACFacebookPermissionsKey: ["email"] ]) { (granted: Bool, error: NSError!) -> Void in
if granted {
let accounts = store.accountsWithAccountType(type)
if let account = accounts.last as? ACAccount {
let pictureURLString = "https://graph.facebook.com/v2.1/me/picture"
let request = SLRequest(forServiceType: SLServiceTypeFacebook, requestMethod: SLRequestMethod.GET, URL: NSURL(string: pictureURLString), parameters: nil)
request.account = account
request.performRequestWithHandler() { (data: NSData!, response: NSHTTPURLResponse!, error: NSError!) -> Void in
if let imageData = data {
// Save the image
// println("Data size: \(imageData.length)\ndata: \(imageData.description)\nAs string: \(NSString(data: imageData, encoding: NSUTF8StringEncoding))")
data.writeToFile(NSFileManager.defaultManager().profileImagePath(), atomically: true)
}
}
}
}
}
Ok, so this versions works, but returns a really, really small version of the profile image. I want a larger image! According to the Facebook docs, and lot's of others on SO the way to do this is to specify parameters such as: type=large or width=120&height=120 but as soon as I do this I get the following error:
{"error":{"message":"An active access token must be used to query information about the current user.","type":"OAuthException","code":2500}}
When the Facebook docs for getting the profile image (at https://developers.facebook.com/docs/graph-api/reference/v2.1/user/picture) explicitly state:
Because profile pictures are always public on Facebook, this call does
not require any access token.
Many suggestions, such as this answer https://stackoverflow.com/a/7882628/1175289, suggest using the Facebook id rather than "me" in the request, but this does not seem to work at all now that we get an app_scoped_user_id rather than the canonical fbId.
EDIT: This works fine, I was just being a plank! :)
For the sake of sanity, here is the code that causes the error:
let store = ACAccountStore()
let type = store.accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierFacebook)
store.requestAccessToAccountsWithType(type, options: [ ACFacebookAppIdKey: "1437725166510606", ACFacebookPermissionsKey: ["email"] ]) { (granted: Bool, error: NSError!) -> Void in
if granted {
let accounts = store.accountsWithAccountType(type)
if let account = accounts.last as? ACAccount {
let pictureURLString = "https://graph.facebook.com/v2.1/me/picture?type=large"
let request = SLRequest(forServiceType: SLServiceTypeFacebook, requestMethod: SLRequestMethod.GET, URL: NSURL(string: pictureURLString), parameters: nil)
request.account = account
request.performRequestWithHandler() { (data: NSData!, response: NSHTTPURLResponse!, error: NSError!) -> Void in
if let imageData = data {
// Save the image
// println("Data size: \(imageData.length)\ndata: \(imageData.description)\nAs string: \(NSString(data: imageData, encoding: NSUTF8StringEncoding))")
data.writeToFile(NSFileManager.defaultManager().profileImagePath(), atomically: true)
}
}
}
}
}
as you can see, the only thing that has changed is the addition of ?type=large to the url string.
If anyone has faced a similar issue, or has any idea what I'm doing wrong, help would be very much appreciated! :)

Because you are using /me/ in your API call, an access_token is required because the API doesn't know who me is. If you replace this with a User ID, e.g.
https://graph.facebook.com/v2.1/4/picture?type=large
It should work fine.
If you want to continue using /me/ in the URL, just append the user's access_token to the URL too, e.g.:
https://graph.facebook.com/v2.1/4/picture?type=large&access_token=abcdef

Related

How to download images from AWS S3 in swift?

is there a good function to download images from AWS S3 bucket? I have an access key and a secret key for permisson. The URL is thru a different database accessible. I also already imported AWSS3 and AWSCore.
I have already found a upload function:
func uploadFile(withImage image: UIImage) {
let access = "access_key"
let secret = "secret_key"
let credentials = AWSStaticCredentialsProvider(accessKey: access, secretKey: secret)
let configuration = AWSServiceConfiguration(region: AWSRegionType.EUCentral1, credentialsProvider: credentials)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let s3BucketName = "bucket_name"
let compressedImage = image.resizedImage(newSize: CGSize(width: 80, height: 80))
let data: Data = compressedImage.pngData()!
let remoteName = generateRandomStringWithLength(length: 12)+"."+data.format
print("REMOTE NAME : ",remoteName)
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = { (task, progress) in
DispatchQueue.main.async(execute: {
// Update a progress bar
})
}
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
// Do something e.g. Alert a user for transfer completion.
// On failed uploads, `error` contains the error object.
})
}
let transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadData(data, bucket: s3BucketName, key: remoteName, contentType: "image/"+data.format, expression: expression, completionHandler: completionHandler).continueWith { (task) -> Any? in
if let error = task.error {
print("Error : \(error.localizedDescription)")
}
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(s3BucketName).appendingPathComponent(remoteName)
if let absoluteString = publicURL?.absoluteString {
// Set image with URL
print("Image URL : ",absoluteString)
}
}
return nil
}
}
I would not recommend to download files directly from S3 using an access and secret key.
I'd propose you do the following:
Make sure the bucket is as "private" as can be.
Have an API with authentication and authorisation (AWS API Gateway) that checks if the user is authenticated and permitted to download the S3 object.
Generate a pre-signed download URL with that is only valid for a short period of time (15-60 minutes).
Return that pre-signed download URL to your app through the API.
Use the URL within your app to download the S3 object.
This way you don't have to ship username and password in your app and the bucket is closed off to the "outside" reducing the risk of accidental information leakage.
Why I wouldn't recommend using the access key and secret key:
This is a potential security issue. People that reverse engineer the app could gain access to those "static" keys and depending on the underlying IAM role do all sorts of harm. But even if you have proper IAM roles with very limited access, essentially shipping a username and password with your app is not a good idea under any circumstance. How would you "rotate" the secret if something bad happens etc.

Sandbox and Finder alias

I'm trying to create a security scoped URL for a user provide file which happens to be an alias using this methods which uses a resolvedFinderAlias() method:
func storeBookmark(url: URL) -> Bool
{
// Resolve alias before storing bookmark
let origURL = (url as NSURL).resolvedFinderAlias()
// Peek to see if we've seen this key before
if let data = bookmarks[url] {
if self.fetchBookmark(key: url, value: data) {
Swift.print ("= \(url.absoluteString)")
return true
}
}
do
{
let options:URL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let data = try url.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: origURL)
bookmarks[url] = data
return self.fetchBookmark(key: url, value: data)
}
catch let error
{
NSApp.presentError(error)
Swift.print ("Error storing bookmark: \(url)")
return false
}
}
which throws an error in attempt to use the resolved URL to as the relative URL; I had originally just swapped the passed in URL to the origURL which ddin't work either.
The only solution is to not do this, or to previously be passed in the original URL. It's almost as if you cannot swap URLs you must be supplied that from either an open dialog or a pasteboard drop.
Are URLs which are aliases not suitable for sandbox work ?

How do I get my AWS Cognito access token for updating user attributes?

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

Get larger Facebook image through Firebase login (swift)

I'm doing a login with Facebook through the Firebase login.
I do:
FBSDKLoginManager().logIn(withReadPermissions: [...], from: self) { (result, error) in
guard let current = FBSDKAccessToken.current() else { return }
guard let token = current.tokenString else { return }
let credential = FIRFacebookAuthProvider.credential(withAccessToken: token)
FIRAuth.auth()?.signIn(with: credential) { (user, error) in
//user.photoURL
}
}
user.photoURL contains the url of the image, but it is very small (100x100), I would like to retrieve the bigger image.
On this stackoverflow post they say to add ?type=large to the url but it doesn't work.
Any solution?
If you want to go through Facebook Graph then you can get the larger image from url
Here is an example of that, in this url you just need to pass your id, you will get larger image
http://graph.facebook.com/yourId/picture?width=400&height=400
For more info you can check this link

declined Permission handling Facebook Login

I am trying to force my users to provide their data in order to get access to my app.
Since the syntax in Swift 3 changed a little, I am stuck in developing this. Basically my idea is the following:
let permissions = ["public_profile", "user_birthday", "user_photos"]
PFFacebookUtils.logInInBackground(withReadPermissions: permissions, block:
{ (user, error) -> Void in
// casting user to FBSDKLoginManagerLoginResult
// asking for specific data declined/granted Permissions
if let user = user as? FBSDKLoginManagerLoginResult {
print(user.declinedPermissions)
print(user.grantedPermissions)
}}
My print method will never be called. What is the real way to cast from user(PFUser?) to FBSDKLoginManagerLoginResult ?
Try getting back the actual FBSDKLoginManagerLoginResult as the result instead of user.
let login:FBSDKLoginManager = FBSDKLoginManager()
let permissions = ["public_profile", "user_birthday", "user_photos"]
login.logIn(withReadPermissions: permissions, from: self) { (result: FBSDKLoginManagerLoginResult?, error: Error?) -> Void in
if(FBSDKAccessToken.current() != nil){
let permissions = FBSDKAccessToken.current().permissions
print(permissions)
//Do whatever else you need to do with the result
}