Spotify createPlaylistWithName results in forbidden error - ios

I am trying to create a playlist with the Spotify iOS SDK, but am receiving the following error:
Error Domain=com.spotify.ios-sdk Code=403 "forbidden" UserInfo=0x7fb9eb577cf0 {NSLocalizedDescription=forbidden}
Here is the way that I am trying to create the playlist:
[SPTRequest playlistsForUserInSession:session callback:^(NSError *error, SPTPlaylistList *myPublicPlaylists) {
[myPublicPlaylists createPlaylistWithName:playlistName publicFlag:FALSE session:session callback:^(NSError *error, SPTPlaylistSnapshot *myRequestedPlaylist) {
if (error != nil) {
return;
}
success(myRequestedPlaylist);
}];
}];
I have verified that the session is valid and the SPTPlaylistlist returned from playlistsForUserInSession is correct. What is the correct method for creating a playlist to avoid this error?

As per the Readme from the iOS SDK release, "When connecting a user to your app, you must provide the scopes your application needs to operate. A scope is a permission to access a certain part of a user's account, and if you don't ask for the scopes you need you will receive permission denied errors when trying to perform various tasks."
In this case, it is necessary to give permissions for either (or both) SPTAuthPlaylistModifyPrivateScope and SPTAuthPlaylistModifyPublicScope.
For example:
// Create SPTAuth instance; create login URL and open it
SPTAuth *auth = [SPTAuth defaultInstance];
NSURL *loginURL = [auth loginURLForClientId:kClientId
declaredRedirectURL:[NSURL URLWithString:kCallbackURL]
scopes:#[SPTAuthStreamingScope, SPTAuthPlaylistModifyPrivateScope, SPTAuthPlaylistModifyPublicScope]];

Related

Download File from AWS S3 Access Denied

I am trying to use Amazon web service to store files for my iOS App.
Here's the code I am using to download the file stored on AWS S3
I added the following to the appDelegate
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc]
initWithRegionType:AWSRegionUSEast2
identityPoolId:#"us-east-2:3764e0f9-khu97-4844-b9f7-57defdfjv8b8b"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast2 credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
Then in my class I used the following to download the object. Kindly note that no encryption is applied to the object and all permissions are unblocked for the bucket
- (void)downloadImageToAWS{
// AWS Configurations
AWSS3DownloadHelper *aws = [[AWSS3DownloadHelper alloc] init];
aws.bucket = #"my-sample-bucket-002";
aws.key = #"photo-sam-002.jpg";
// AWS progress block
aws.progressBlock = ^(AWSS3TransferUtilityTask *task, NSProgress *progress) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"progress.fractionCompleted %f", progress.fractionCompleted);
});
};
// AWS completionHandler
[self addAWSDownloadComplitionHandler:aws];
// Update UI if job / task can upload a file on AWS S3
[self successfulDownloadOfAWSS3ByCompletionHandler:aws.completionHandler withProgressBlock:aws.progressBlock];
[aws downloadAWSFile];
}
- (void) addAWSDownloadComplitionHandler:(AWSS3DownloadHelper *)aws {
// Create instance to View Controller
NSLog(#"addAWSDownloadComplitionHandler");
aws.completionHandler = ^(AWSS3TransferUtilityDownloadTask *task, NSURL *location, NSData *data, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
NSLog(#"Unsuccessfully downloaded error %li", (long)[error code]);
NSLog(#"Unsuccessfully downloaded error %#", [error description]);
}
else if (data) {
NSLog(#"data %#", data);
}
});
};
}
- (void) successfulDownloadOfAWSS3ByCompletionHandler:(AWSS3TransferUtilityDownloadCompletionHandlerBlock)completionHandler withProgressBlock:(AWSS3TransferUtilityProgressBlock)progressBlock {
NSLog(#"successfulDownloadOfAWSS3ByCompletionHandler");
AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
[transferUtility enumerateToAssignBlocksForUploadTask:nil downloadTask:^(AWSS3TransferUtilityDownloadTask * _Nonnull downloadTask, AWSS3TransferUtilityProgressBlock _Nullable __autoreleasing * _Nullable downloadProgressBlockReference, AWSS3TransferUtilityDownloadCompletionHandlerBlock _Nullable __autoreleasing * _Nullable completionHandlerReference) {
NSLog(#"taskIdentifier %lu", (unsigned long)downloadTask.taskIdentifier);
*downloadProgressBlockReference = progressBlock;
*completionHandlerReference = completionHandler;
dispatch_async(dispatch_get_main_queue(), ^{
});
}];
}
Although I have put all permissions to allow. Block all public access is turned off, I keep getting the following error:
Domain=com.amazonaws.AWSS3TransferUtilityErrorDomain Code=2 "(null)" UserInfo={Server=AmazonS3, Error={
Code = AccessDenied;
HostId = "SJABFLSKBtprmLRaHLjjockzjfubejlakhipjaKDNSAFJLB4ViE=";//Changed
Message = "Access Denied";
RequestId = jabduw2dhC6WCT;//changed
}, Transfer-Encoding=Identity, Content-Type=application/xml, Date=Wed, 29 Dec 2021 09:33:06 GMT, x-amz-request-id=2TJSHCJ2ASTC6WCT, x-amz-id-2=Fbg2cDXwU5wLgQLHbGtprmLRaHLjjocvZzCcUNfMrSpT5Oiwl3LjEkpPQ2OBzLmBrKXnrwq4ViE=}
I have applied all suggestions in this video:
Why am I getting an Access Denied error from the Amazon S3 console while I modify a bucket policy? - YouTube
Trying the following command on the cloud shell didn't show any error:
aws s3 cp s3://<bucket name>/<key> /tmp/localfile
Firstly, let's rule out coding. Try to access the file natively using the AWS CLI. Just a straight up aws s3 cp s3://<bucket name>/<key> /tmp/localfile
If this works, you've got a code issue, and we continue checking. If it does not work, you may have a permissions issue.
The 2nd thing to check - are you using encryption? Chances are (from my experience) that while you may have permissions to access the S3 bucket, you may not have permissions to access the encryption key, so the access denied you're getting is actually coming from KMS. You may need to allow the right to decrypt the KMS key.
It seems you are using Cognito as the credentials provider. As I understand it, even for public buckets, if you access through API, you need to setup proper IAM roles and permissions. That means assigning the IAM roles with S3 access to the authenticated identities inside the identity pool in Cognito.
Or, you have to pass the no-sign-request. At least in CLI you have to do so. https://docs.aws.amazon.com/cli/latest/reference/.
But if you access using URL, with public buckets, you will be able to get the objects.
In cognito Identity pool you can specify two IAM Roles (even more, but lets focus on default two):
Unauthenticated role - the role assigned to user which is not logged in to Identity Provider
Authenticated role - the role assigned to authenticated user
Default roles created by Cognito don't provide access to S3 buckets - you have to modify policy and add missing permissions (i.e. s3:PutObject, s3:GetObject, s3:ListBucket)
If your bucket is encrypted with KMS key, your role policy or key polisy must allow kms actions (i.e. kms.Encrypt, kms:Decrypt, kms:generateKeyData)
Try to invoke sts getCallerIdentity action in your app and check returned identity, it should return Authenticated role ARN.
If so, add missing permissions to access S3 bucket and KMS key to returned role policy.

iOS ADAL-Make silent call using refresh token

I am using iOS ADAL library version 2.2.6 and receiving refresh token upon successful login. Now I want to make a silent call by using this refresh token. I tried with following method but it fails to return the access token.
ADAuthenticationContext *authContext;
[authContext acquireTokenSilentWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
userId:strUserID //loggedIn userID
completionBlock:^(ADAuthenticationResult *result){
// It alway throws an error //Please call the non-silent acquireTokenWithResource methods.
if(result.error){
ADAuthenticationError *error = nil;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:inputData.authority error:&error];
[authContext acquireTokenWithResource:inputData.ResourceID
clientId:inputData.ClientId // Comes from App Portal
redirectUri:inputData.RedirectUri // Comes from App Portal
completionBlock:^(ADAuthenticationResult *result)
{
if (AD_SUCCEEDED != result.status){
// Show alert with error description
}
else{
//Handle Success token
}
}];
}else{
//Handle Success token
}
}];
But it always throws an error saying "The user credentials are needed to obtain access token. Please call the non-silent acquireTokenWithResource methods."
Is there any way to make a silent call using refresh token? please help me on it. Thanks in advance.
When you use Microsoft's authentication libraries, you should always first check to see if there is a user in the cache that can be used for your resource before prompting the user to sign in. This allows us to check if the user had previously signed in to your app or if there are other apps that share state with your app that may have already asked the user to sign in elsewhere.
If the user is found, we will try to acquire a token without interrupting the user at all. Sometimes a user will have changed their password or done some other action that will require them to sign in again even if they have signed in to your app previously. This is what you are seeing. The library is telling you that for the user you are trying to acquire a token for, they need to sign in again to make something right.
In order to handle all these cases elegantly, we recommend that you use the pseudocode pattern of:
acquireTokenSilent()
(if error InteractiveAuthenticationRequired) {
acquireTokenInteractively() }
The pattern first checks if a user you specify is available in the token cache. If it is, we then call the Azure Active Directory service to see if the Refresh token for that user is valid. If both of these are true, then the user is signed in silently. If the user isn't found or the server rejects the Refresh Token, then an error is sent from the library that indicates the user needs to sign in interactively.
In the above, you are doing this first part, but you aren't handling the case where the user needs to sign in if there is a problem.
The best way is to catch the error with a ADErrorCode of AD_ERROR_USER_INPUT_NEEDED
Here is a code sample on how to do this pattern.
// Here we try to get a token from the stored user information we would have from a successful authentication
[authContext acquireTokenSilentWithResource:data.resourceId
clientId:data.clientId
redirectUri:redirectUri
userId:data.userItem.userInformation.userId
completionBlock:^(ADAuthenticationResult *result) {
if (!result.error)
{
completionBlock(result.tokenCacheStoreItem.userInformation, nil);
} else {
if ([result.error.domain isEqual:ADAuthenticationErrorDomain] && result.error.code == AD_ERROR_USER_INPUT_NEEDED) {
// Here we know that input is required because we couldn't get a token from the cache
[authContext acquireTokenWithResource:data.resourceId
clientId:data.clientId
redirectUri:redirectUri
userId:data.userItem.userInformation.userId
completionBlock:^(ADAuthenticationResult *result) {
if (result.status != AD_SUCCEEDED)
{
completionBlock(nil, result.error);
}
else
{
data.userItem = result.tokenCacheStoreItem;
completionBlock(result.tokenCacheStoreItem.userInformation, nil);
}
}];
} else {
completionBlock(nil, result.error);
}
}
}];
Keep in mind this code is very verbose. You will most likely want to have acquireTokenWithResource: a separate method that you could call with [self acquireTokenWithResource]

requestAccessToAccountsWithType return error code 7

I'm using [ACAccountStore requestAccessToAccountsWithType] to get Facebook users info for users had logged in iOs Facebook settings. It still work fine until last week, now it always return error code 7.
Error Domain=com.apple.accounts Code=7 "The Facebook server could not fulfill this access request: remote_app_id does not match stored id (404)" UserInfo=0x7fef59d8a760 {NSLocalizedDescription=The Facebook server could not fulfill this access request: remote_app_id does not match stored id (404)}
I do not know why this happen, and we not changing anything in our settings in developers.facebook.com.
This is my code line.
NSDictionary *options = #{
ACFacebookAppIdKey : [[NSBundle mainBundle] objectForInfoDictionaryKey:#"FacebookAppID"],
ACFacebookPermissionsKey : #[#"email"],
ACFacebookAudienceKey: ACFacebookAudienceFriends
};
ACAccountType *FBaccountType= [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
[self.accountStore requestAccessToAccountsWithType:FBaccountType options:options completion:
^(BOOL granted, NSError *error) {
if (granted) {
} else {
// it always return not granted.
}
}
}];
I don't know why it is happening now.
I have check something from this iOS 6 Facebook posting procedure ends up with "remote_app_id does not match stored id" and this Getting error ACErrorPermissionDenied (error code 7) for Facebook when using Social Framework but not found any luck.
The issue is either of the follows:
there is mismatch between the app_id entered in your xcode project and the app_id you have in developers.facebook.com. Make sure you have setup the plist file properly as pointed out in the Facebook documentation. https://developers.facebook.com/docs/ios/getting-started/#configure
Create a key called FacebookAppID with a string value, and add the app ID there.
Create a key called FacebookDisplayName with a string value, and add the Display Name you configured in the App Dashboard.
Create an array key called URL types with a single array sub-item called URL Schemes. Give this a single item with your app ID prefixed with fb.
If your app_id matches properly, make sure to enter the app store ID in your settings at developers.facebook.com.

Spotify API getting starred list not authorized

I'm using the Spotify iOS SDK and trying to get use this function starredListForUserInSession:callback: to find a user's starred list once they have logged in.
The actual logging in of the user is working correctly, as I am able to stream Spotify tracks and a valid SPTSession is returned. I am including both the streaming and the playlist reading scopes as seen below:
SPTAuth *auth = [SPTAuth defaultInstance];
NSURL *loginPageURL = [auth loginURLForClientId:kSpotifyClientID
declaredRedirectURL:[NSURL URLWithString:kSpotifyCallbackURL]
scopes:#[SPTAuthStreamingScope, SPTAuthPlaylistReadPrivateScope]];
However, when I use the function:
[SPTRequest starredListForUserInSession:[AppDelegate sharedAccounts].spotifySession callback:^(NSError *error, id object) {
if (error) {
NSLog(#"%#", error);
}
}];
I consistently receive the following error:
"The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x7a8cbf50 {NSErrorFailingURLKey=https://api.spotify.com/v1/users/(null)/starred, NSErrorFailingURLStringKey=https://api.spotify.com/v1/users/(null)/starred, NSUnderlyingError=0x7a6d5b60 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1012.)"
When I looked this up, it said that it means this request requires authentication, though the user has already been authenticated with the presumably correct scope. What am I missing?

iOS8 Touch ID getting error : Pending UI mechanism already set

Description of error is below:
Error Domain=com.apple.LocalAuthentication Code=-1000 "Pending UI mechanism already set." UserInfo=0x17406b0c0 {NSLocalizedDescription=Pending UI mechanism already set.}
I am also trying Apple's Sample Example app and getting same error. Previously it was working fine, but it has stopped working suddenly ad not working. Please help.
I am using iPhone 6 with iOS 8.1
This code just worked fine for me.
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = #"String explaining why app needs authentication";
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL success, NSError *error) {
if (success) {
// User authenticated successfully, take appropriate action
NSLog(#"User authenticated successfully, take appropriate action");
} else {
// User did not authenticate successfully, look at error and take appropriate action
NSLog(#"User did not authenticate successfully, look at error and take appropriate action");
}
}];
} else {
// Could not evaluate policy; look at authError and present an appropriate message to user
NSLog(#"Could not evaluate policy: %#",authError);
}
Don't forget to import Local Authentication framework <LocalAuthentication/LAContext.h>. Hope this will solve your issue.
Try rebooting your phone.
I also started getting this error and decided to see if other apps were affected. I have both Dropbox and Mint set up for Touch ID. Sure enough Touch ID wasn't working for them either and they were falling back to passcodes.
I rebooted my phone and it started working again, so it would seem the Touch ID can bug out and stop working. I'm on iOS 8.2 btw.
I guess the proper way to handle this condition is like those apps do and fallback to password / passcode.

Resources