I am trying to use Cognito federated identities to authenticate with Facebook and create a user in a cognito user pool and map user attributes.
The AWS Congnito service configuration, I believe is correct as I have it working perfectly with a web app. However when trying the same with iOS app despite all working in the code and authenticating with Facebook and assuming an authenticated role, no user is created in the pool.
I am using the following code flow detail below as per the Cognito "Basic (Classic) Authflow" Is this correct approach, to have a user created in the user pool?
getId, getOpenIdToken, assumeRoleWithWebIdentity.
AWSCognitoIdentityGetIdInput *input = [[AWSCognitoIdentityGetIdInput alloc] init];
[input setIdentityPoolId:poolId];
[input setAccountId:#"XXXXXXXXXXXX"];
NSDictionary *logons = #{#"graph.facebook.com":tknStr};
[input setLogins:logons];
AWSCognitoIdentity *id = [AWSCognitoIdentity defaultCognitoIdentity];
[id getId:input completionHandler:^(AWSCognitoIdentityGetIdResponse * _Nullable response, NSError * _Nullable error) {
if (error)
{
//handle the error
}
else
{
AWSCognitoIdentityGetCredentialsForIdentityInput *getCredsInput = [AWSCognitoIdentityGetCredentialsForIdentityInput new];
[getCredsInput setCustomRoleArn:#"arn:aws:iam::XXXXXXXXX:role/XXXXXXXXXXXXXXX”];
[getCredsInput setIdentityId:[response identityId]];
[getCredsInput setLogins:logons];
AWSCognitoIdentityGetOpenIdTokenInput *openID = [AWSCognitoIdentityGetOpenIdTokenInput new];
[openID setIdentityId:[response identityId]];
[openID setLogins:logons];
[id getOpenIdToken:openID completionHandler:^(AWSCognitoIdentityGetOpenIdTokenResponse * _Nullable response, NSError * _Nullable error) {
if (error)
NSLog(#"task.error - %#",error);
else
{
AWSSTS *sts = [AWSSTS defaultSTS];
AWSSTSAssumeRoleWithWebIdentityRequest *request = [[AWSSTSAssumeRoleWithWebIdentityRequest alloc] init];
[request setRoleArn:#"arn:aws:iam::XXXXXXXXX:role/XXXXXXXXXXXXXXX”];
[request setRoleSessionName:#"ginger55"];
[request setWebIdentityToken:[response token]];
[sts assumeRoleWithWebIdentity:request completionHandler:^(AWSSTSAssumeRoleWithWebIdentityResponse * _Nullable response, NSError * _Nullable error) {
if (error)
{
NSLog(#"task.error - %#",error);
}
else
{
NSLog(#“response = %#",response);
}
}];
}
}];
}
}];
Any help appreciated.
Related
My boss said at iOS beta version 13.2 can read bank credit card info by iPhone's NFC function. But I don't know how to achieve it.
What I know might work method is NFCTagReaderSession, but it doesn't work at following codes:
[self.tagSes invalidateSession];
self.tagSes = [[NFCTagReaderSession alloc]initWithPollingOption:NFCPollingISO15693 delegate:self queue:dispatch_get_main_queue()];
if (NFCTagReaderSession.readingAvailable) {
self.tagSes.alertMessage = #"send card to phone's back";
[self.tagSes beginSession];
} else {
NSLog(#"no support NFC");
}
- (void)tagReaderSession:(NFCTagReaderSession *)session didDetectTags:(NSArray<__kindof id<NFCTag>> *)tags{
NSLog(#"%#\n",tags);
self.curentTag = [tags firstObject];
if (self.curentTag.type == NFCTagTypeMiFare) {
id<NFCMiFareTag> mifareTag = [self.curentTag asNFCMiFareTag];
NSData *data = mifareTag.identifier;
NSLog(#"data:%#",data);//** 1. there can print card type is NFCMiFareTag **
[session connectToTag:self.curentTag completionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(#"fail:%#",error);//** 2. then ,in here throw a fail,tell me connect break **
return ;
}
[mifareTag readNDEFWithCompletionHandler:^(NFCNDEFMessage * _Nullable messages, NSError * _Nullable error) {
NSLog(#"--->>%#,%#",messages,error);
NSArray *ary = messages.records;
for (NFCNDEFPayload *rec in ary) {
NFCTypeNameFormat typeName = rec.typeNameFormat;
NSData *payload = rec.payload;
NSData *type = rec.type;
NSData *identifier = rec.identifier;
NSLog(#"TypeName : %d",typeName);
NSLog(#"Payload : %#",payload);
NSLog(#"Type : %#",type);
NSLog(#"Identifier : %#",identifier);
}
}];
}];
}
}
Hi I'm using mobile buy sdk for my eCommerce app.After login I try to save Customer in model manager by using the following code
[self.client loginCustomerWithCredentials:credentials callback:^(BUYCustomer * customer, BUYCustomerToken * token, NSError * _Nullable error) {
if (customer && !error) {
[self.client.modelManager insertCustomersWithJSONArray:customer];
[self.client.modelManager insertCustomerWithJSONDictionary:customer];
}else{
[self showEror:#"LogIn Failed" message:#"Please provide valid Details"];
}
}];
My problem is BuyCustomer is a class i have to convert it into either JsonArray or JsonDictionary so then only I can able to save it. How can I convert BuyCustomer class into either one of above and save it? Thanks in advance.
if you have customer logged in you can use following code to get customer
[self.client getCustomerCallback:^(BUYCustomer * _Nullable _customer, NSError * _Nullable error) {
if (error) {
NSLog(#"Customer error");
}else{
NSLog(#"Customer available");
self.customer = _customer;
}
}];
I'm developing an iOS app that needs to read User data from MS Azure Active Directory.
I have successfully followed some examples on iOS app from the MS Azure documentation and successfully brought up their authentication page and have the user signed in. What I get back is some user data in the form of a ADUserInformation object.
Here's is the code I have:
NSString *authority = #"https://login.microsoftonline.com/a5960f61-0bf9-4bf6-96cd-98c61d30XXXX/federationmetadata/2007-06/federationmetadata.xml";
NSString *resourceId = #"74cd2559-0389-4871-9904-bc767d71XXXX"; // (server)
NSString *clientId = #"c8a956a7-84b7-4050-875c-896aab6bXXXX"; //ios-client (us)
NSURL *redirectUri = [[NSURL alloc]initWithString:#"https://XXXXevents.azurewebsites.net/.auth/login/done"];
ADAuthenticationError *error;
ADAuthenticationContext * authContext = [ADAuthenticationContext authenticationContextWithAuthority:authority error:&error];
//authContext.parentController = parent;
[ADAuthenticationSettings sharedInstance].enableFullScreen = YES;
[authContext acquireTokenWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (result.status != AD_SUCCEEDED) {
NSLog(#"%#", result);
return;
}
else {
//save all of this information into core data
NSDictionary * payload = #{#"access_token" : result.tokenCacheItem.accessToken};
NSLog(#"%#", payload);
//#"aad"
//#"windowsazureactivedirectory"
[[QSActivityService defaultService].client loginWithProvider: #"aad"
token: payload
completion: ^(MSUser * _Nullable user, NSError * _Nullable error) {
NSLog(#"loginWithProvider-------");
if(!error) {
NSLog(#"YAY! %s - user: %# ", __FUNCTION__, user.userId);
ADUserInformation * temp = result.tokenCacheItem.userInformation;
[[CoreDataStack defaultStack] updateUserDetailFamilyName:temp.allClaims[#"family_name"]
version:temp.allClaims[#"ver"]
email:temp.allClaims[#"email"]
nbf:temp.allClaims[#"nbf"]
exp:temp.allClaims[#"exp"]
givenName:temp.allClaims[#"given_name"]
idp:temp.allClaims[#"idp"]
ipaddr:temp.allClaims[#"ipaddr"]
iss:temp.allClaims[#"iss"]
oid:temp.allClaims[#"oid"]
typ:temp.allClaims[#"typ"]
sub:temp.allClaims[#"sub"]
amr:temp.allClaims[#"amr"]
aud:temp.allClaims[#"aud"]
alg:temp.allClaims[#"alg"]
iat:temp.allClaims[#"iat"]
tid:temp.allClaims[#"tid"]
name:temp.allClaims[#"name"]
uniqueName:temp.allClaims[#"unique_name"]];
//other code, no problems here
MS Graph API
However, I would like access profile images, and all the other data. I have read that MS Graph API provides it, but I'm not sure how and where I would put the token.
Do I use the token from result.tokenCacheItem.accessToken? If so, in the header? or body?
Or do I simply hit up graph.windows.com twice. First time to get the Authentication Token, and second time for the data?
I have read a lot of documentation and none of them works as I keep getting the Token Missing or Malformed error message.
My Graph API code looks like this:
-(void)getUsersUsingAccessToken:(NSDictionary*)token completion:(void (^) (void))completion {
NSString * tenant = #"a5960f61-0bf9-4bf6-96cd-98c61d306f12";
NSString * accessToken = token[#"access_token"];
NSString * urlString = [NSString stringWithFormat: #"https://graph.windows.net/%#/tenantDetails?api-version=1.6", tenant];
NSString * httpVerb = #"POST";
//build an info object and convert to json
NSDictionary * bodyFormDict
= [NSDictionary dictionaryWithObjectsAndKeys:
#"client_credentials", #"grant_type",
#"https://graph.windows.net", #"resource",
#"c8a956a7-84b7-4050-875c-896aab6xxxx", #"client_id",
#"XLlZl69aUKiQTo4dpeiprItm+LYbDtpt6e9dn0bxxxx", #"client_secret",
nil];
NSError *error = nil;
//1st step
NSData * jsonInputData = [NSJSONSerialization dataWithJSONObject:bodyFormDict
options:NSJSONWritingPrettyPrinted
error:&error];
//2nd step
NSString * httpBodyString = [[NSString alloc]
initWithData:jsonInputData
encoding:NSUTF8StringEncoding];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.allowsCellularAccess = YES;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
request.HTTPMethod = httpVerb;
[request setValue: #"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setValue: accessToken forHTTPHeaderField:#"Authorization: Bearer"];
[request setHTTPBody:[httpBodyString dataUsingEncoding:NSUTF8StringEncoding]];
//asynchronous
NSURLSessionDataTask * getDataTask = [self.session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
//other code
}
If someone can provide working code sample using objective c to successfully retrieve data from the MS Graph API, it would be a great help.
Thanks for your time!
I believe the problem you are having is that the http header field isn't set correctly. Try this -
NSString *authValue = [NSString stringWithFormat:#"Bearer %#", accessToken];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
I found my answers in MS MSDN's starter projects and code samples
https://msdn.microsoft.com/en-us/office/office365/howto/starter-projects-and-code-samples
The specific project that helped me is this:
https://github.com/OfficeDev/O365-iOS-Microsoft-Graph-Profile
As you are going through that sample keep in mind:
Replace INSERT-AUTHORITY-HERE - the name of the tenant in which you provisioned your application. The format should be https://login.windows.net/(YourAzureUserName).onmicrosoft.com
I have found that
https://login.microsoftonline.com/YourAzureAccountID/federationmetadata/2007-06/federationmetadata.xml
also works
Replace INSERT-RESOURCE-ID-HERE - the ID for your mobile app backend. This is the Web API service app ID. NOT the native client iOS app.
Replace INSERT-CLIENT-ID-HERE - the client ID you copied from your iOS NATIVE client application. NOT the Web API service app.
Replace INSERT-REDIRECT-URI-HERE - your site’s /.auth/login/done endpoint, using the HTTPS scheme. This value should be similar to
#"https://XXXXXXXXXX.azurewebsites.net/.auth/login/done"
IF you have trouble importing the ADAL framework...
http://shanghaiseagull.com/index.php/2016/05/11/import-another-project-framework-into-your-project/
library can be found here: https://github.com/AzureAD/azure-activedirectory-library-for-objc
Hope it helps someone starting out...and please let me know if I can be of further help.
I have this in AppDelegate.m
[QBSettings setApplicationID:xxxxxx];
[QBSettings setAuthKey:#"dsad"];
[QBSettings setAuthSecret:#"asdd"];
[QBSettings setAccountKey:#"asdsda"];
[QBSettings setLogLevel:QBLogLevelNothing];
[QBRequest logInWithUserLogin:#"testuser" password:#"testuser" successBlock:^(QBResponse *response, QBUUser *user) {
if (user) {
user.login = #"testuser";
user.password = #"testuser";
// __typeof(self) strongSelf = weakSelf;
[[QBChat instance] connectWithUser:user completion:^(NSError * _Nullable error) {
}
];
}
} errorBlock:^(QBResponse * _Nonnull response) {
}];
and this trying to send a message in another ViewController.m
QBChatMessage *messagetosend = [QBChatMessage message];
messagetosend.senderID = 10516336;
messagetosend.senderNick = #"Andrey M.";
messagetosend.text = #"test test";
messagetosend.dateSent = [NSDate dateWithTimeInterval:-12.0f sinceDate:[NSDate date]];
QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:#"56d9e95ba28f9a7bf5000058" type:QBChatDialogTypePublicGroup];
[chatDialog joinWithCompletionBlock:^(NSError * _Nullable error) {
[chatDialog sendMessage:messagetosend completionBlock:^(NSError * _Nullable error) {
NSLog(#"%#",[error localizedDescription]);
}];
}];
but the message doesn't sent to the dialog I check in quickblox dashboard it doesn't work. I used PHP framework and it works but in the iOS i do not know where it the problem, must open a session or what ?
FYI: this dialog ID " 56d9e95ba28f9a7bf5000058 " from Quickblox dashboard it isn't randomly or fake
Thanks
In order to send message in group chat dialog you should create dialog on a server.
Dialog must contain occupantIDs
+ (QB_NONNULL QBRequest *)createDialog:(QB_NONNULL QBChatDialog *)dialog
successBlock:(QB_NULLABLE void(^)(QBResponse * QB_NONNULL_S response, QBChatDialog * QB_NULLABLE_S createdDialog))successBlock
errorBlock:(QB_NULLABLE QBRequestErrorBlock)errorBlock;
You must send custom parameters
messagetosend.senderID = 10516336;
messagetosend.senderNick = #"Andrey M.";
messagetosend.text = #"test test";
/** You will see the on dashboard after you set save_to_history to true **/
messagetosend.customParameters = ["application_id":kQBApplicationID, "save_to_history":true]
Let me know if this works for you.
Trying to Authenticate with Azure Active Directory and fetch mail, calendar data, accessToken is returned successfully:
authority = #"https://login.windows.net/common/oauth2/authorize";
redirectUriString = #"http://xxxxxx.xxxxxxx.com/oauth";
resourceId = #"https://outlook.office365.com";
clientId = #"xxxxxxx-xxxxx-xxx";
-(void) getToken : (BOOL) clearCache completionHandler:(void (^) (NSString*))completionBlock;
{
ADAuthenticationError *error;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:authority
error:&error];
[authContext setValidateAuthority:YES];
NSURL *redirectUri = [NSURL URLWithString:redirectUriString];
if(clearCache){
[authContext.tokenCacheStore removeAllWithError:&error];
if (error) {
NSLog(#"Error: %#", error);
}
}
[authContext acquireTokenWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (AD_SUCCEEDED != result.status){
// display error on the screen
[self showError:result.error.errorDetails];
}
else{
completionBlock(result.accessToken);
}
}];
}
-(NSArray*)getEventsList
{
__block NSMutableArray * todoList;
[self getToken:YES completionHandler:^(NSString* accessToken){
NSURL *todoRestApiURL = [[NSURL alloc]initWithString:#"https://outlook.office365.com/api/v1.0/me/folders/inbox/messages?$top=2"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:todoRestApiURL];
NSString *authHeader = [NSString stringWithFormat:#"Bearer %#", #""];
[request addValue:authHeader forHTTPHeaderField:#"Authorization"];
[request addValue:#"application/json; odata.metadata=none" forHTTPHeaderField:#"accept"];
[request addValue:#"fbbadfe-9211-1234-9654-fe435986a1d6" forHTTPHeaderField:#"client-request-id"];
[request addValue:#"Presence-Propelics/1.0" forHTTPHeaderField:#"User-Agent"];
//[request addValue:#"true" forHTTPHeaderField:#"return-client-request-id"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error == nil){
NSArray *scenarios = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
todoList = [[NSMutableArray alloc]initWithArray:scenarios];
//each object is a key value pair
NSDictionary *keyVauePairs;
for(int i =0; i < todoList.count; i++)
{
keyVauePairs = [todoList objectAtIndex:i];
NSLog(#"%#", keyVauePairs);
}
}
NSLog(#"Finished");
//[delegate updateTodoList:TodoList];
}];
}];
return nil; }
Error is returned in response object:
{
error = {
code = ErrorAccessDenied;
message = "Access is denied. Check credentials and try again.";
};
}
I know its late to answer this but it might be helpful for someone like me who was struggling to get the same thing done
I have done this using the office 365 SDK for iOS which has all the inbuilt classes to do your work.
If you download their sample code it will provide you all the details you require to do certain operations (mail, calendar, contacts, one drive).
Before using the SDK make sure you login to Azure AD and register your application and add permissions so that you do not get 403 error code or any access denied message.
I am using the below code to fetch my events details from outlook calendar
[self getClientEvents:^(MSOutlookClient *client) {
NSURLSessionDataTask *task = [[[client getMe] getEvents] read:^(NSArray<MSOutlookEvent> *events, MSODataException *error) {
if (error==nil) {
if (events.count!=0) {
dispatch_async(dispatch_get_main_queue(), ^{
for(MSOutlookEvent *calendarEvent in events){
NSLog(#"name = %#",calendarEvent.Subject);
}
});
}else{
NSLog(#"No events found for today");
}
}
}];
[task resume];
}];
getClientEvents is a method which gives call to the Office 365 SDK and fetches the event details of the user but it first fetches the token for the resource and then makes the call with the acquired token
-(void)getClientEvents : (void (^) (MSOutlookClient* ))callback{
[self getTokenWith : #"https://outlook.office365.com" :true completionHandler:^(NSString *token) {
MSODataDefaultDependencyResolver* resolver = [MSODataDefaultDependencyResolver alloc];
MSODataOAuthCredentials* credentials = [MSODataOAuthCredentials alloc];
[credentials addToken:token];
MSODataCredentialsImpl* credentialsImpl = [MSODataCredentialsImpl alloc];
[credentialsImpl setCredentials:credentials];
[resolver setCredentialsFactory:credentialsImpl];
[[resolver getLogger] log:#"Going to call client API" :(MSODataLogLevel *)INFO];
callback([[MSOutlookClient alloc] initWithUrl:#"https://outlook.office365.com/api/v1.0" dependencyResolver:resolver]);
}];
}
getTokenWith method fetches the token for a resource first and then with the acquired token makes the necessary calls to fetch the events, but before fetching the token it checks in the cache to see if there are any tokens available for the same resource.
// fetch tokens for resources
- (void) getTokenWith :(NSString *)resourceId : (BOOL) clearCache completionHandler:(void (^) (NSString *))completionBlock;
{
// first check if the token for the resource is present or not
if([self getCacheToken : resourceId completionHandler:completionBlock]) return;
ADAuthenticationError *error;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:[[NSUserDefaults standardUserDefaults] objectForKey:#"authority"] error:&error];
NSURL *redirectUri = [NSURL URLWithString:#"YOUR_REDIRECT_URI"];
[authContext acquireTokenWithResource:resourceId
clientId:[[NSUserDefaults standardUserDefaults] objectForKey:#"clientID"]
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (AD_SUCCEEDED != result.status){
[self showError:result.error.errorDetails];
}
else{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:result.tokenCacheStoreItem.userInformation.userId forKey:#"LogInUser"];
[userDefaults synchronize];
completionBlock(result.accessToken);
}
}];
}
getCacheToken method: Checks if there are any reusable token for any resources.
-(BOOL)getCacheToken : (NSString *)resourceId completionHandler:(void (^) (NSString *))completionBlock {
ADAuthenticationError * error;
id<ADTokenCacheStoring> cache = [ADAuthenticationSettings sharedInstance].defaultTokenCacheStore;
NSArray *array = [cache allItemsWithError:&error];
if([array count] == 0) return false;
ADTokenCacheStoreItem *cacheItem;
for (ADTokenCacheStoreItem *item in array) {
if([item.resource isEqualToString:resourceId]){
cacheItem = item;
break;
}
}
ADUserInformation *user = cacheItem.userInformation;
if(user == nil) return false;
if([cacheItem isExpired]){
return [self refreshToken:resourceId completionHandler:completionBlock];
}
else
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:user.userId forKey:#"LogInUser"];
[userDefaults synchronize];
completionBlock(cacheItem.accessToken);
return true;
}
}
Using this code and Office 365 SDK in place you can get the outlook events for a particular user, before that make sure you have full permissions in the Azure AD else you may get 0 events as response.
Please note all the methods are from the SDK example apart from the first method to view how to fetch the events i would recommend to download the exchange example from the github.
You can also use MSGraph SDK to fetch calendars and events:
Check this link: Configuration process is same, only fetching events is different(see given code for fetching events):
How to Fetch/Create calender by O365-iOS-Connect?
Note: Above link is used to fetch calendars from outlook the process is same for this but you should use this code after authentication and completed get events action look like this:
- (IBAction)getCalendarsEvents:(id)sender {
[NXOAuth2AuthenticationProvider setClientId:clientId
scopes:#[#"https://graph.microsoft.com/Files.ReadWrite",
#"https://graph.microsoft.com/Calendars.ReadWrite"]];
[[NXOAuth2AuthenticationProvider sharedAuthProvider] loginWithViewController:nil completion:^(NSError *error) {
if (!error) {
[MSGraphClient setAuthenticationProvider:[NXOAuth2AuthenticationProvider sharedAuthProvider]];
self.client = [MSGraphClient client];
// Authentication done
[[[[_client me] events] request] getWithCompletion:^(MSCollection *response, MSGraphUserEventsCollectionRequest *nextRequest, NSError *error){
NSArray *arr = response.value;
MSGraphEvent *event = arr.firstObject;
// Here you will getting outlook events
}];
}
}];
}