NEVPNManager VPN Config SharedSecret Error - ios

I want to establish a VPN connection with my VPN server.
I installed VPN server from digital oceans.
I want to make VPN application for IOS by using objective-C by using my VPN connection settings.
Here I faced problem:
no VPN shared secret was provided.
Here I Used Codes
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[[NEVPNManager sharedManager] setEnabled:YES];
[[NEVPNManager sharedManager] loadFromPreferencesWithCompletionHandler: ^(NSError *error) {
NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
p.serverAddress =#"178.62.78.101";
p.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
p.useExtendedAuthentication = YES;
NSString *secret = #"gfJL$$";
NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
p.sharedSecretReference=secretData;
p.localIdentifier=#"londra1";
p.username=#"root";
p.passwordReference=[[SSKeychain passwordForService:#"2eee9fa59" account:#"2eee9fa5"] dataUsingEncoding:NSUTF8StringEncoding];
p.disconnectOnSleep = NO;
[NEVPNManager sharedManager].protocolConfiguration=p;
NSMutableArray *rules = [[NSMutableArray alloc] init];
NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new];
[rules addObject:connectRule];
[[NEVPNManager sharedManager] setOnDemandRules:rules];
[[NEVPNManager sharedManager] setLocalizedDescription:#"vpn"];
[[NEVPNManager sharedManager] setEnabled:YES];
[[NEVPNManager sharedManager] saveToPreferencesWithCompletionHandler: ^(NSError *error) {
NSLog(#"Save VPN to preference complete");
if (error) {
NSLog(#"Save error: %#", error);
}
}];
NSError *startError;
[[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
if(startError) {
NSLog(#"Start error: %#", startError.localizedDescription);
}
}];
return YES;
}

You are setting sharedSecretReference as an NSData, but this conflicts with its definition.
NEVPNProtocolIPSec::sharedSecretReference is defined as:
A persistent keychain reference to a keychain item containing the IKE shared secret.
You need to create a keychain item of type kSecClassGenericPassword to use as the shared secret, and provide a persistent reference to the keychain item. A persistent reference is critical here, as it can be stored to disk or passed between processes, which is required for NEVPNManager to access the shared secret and connect your VPN. I suspect that although passwordReference seems to be set by a keychain item, it may not be returning a persistent reference either.
You will need to use SecItemCopyMatching with return type kSecReturnPersistentRef to achieve this.

Related

Google Drive - 403 insufficient permission uploading file but not creating folder

I am trying to add Google Drive support to one of my apps using a private app data folder. I have sign-in working with the GIDSignIn class and the scope set to kGTLRAuthScopeDriveAppdata. Once I am signed in, I can create folders and get a file listing that shows the folders are there, then I can delete the folders and the file listing shows that they are gone. But for some reason when I try to upload a file I get a 403 error ("The user does not have sufficient permissions for this file."). This happens whether I try to put the file in the root of the app data folder or into a folder I have created.
I have set up a project in the Google Developer Console. I have an API key configured to work with my app's bundle ID and given it unrestricted API access. The Google Drive API is enabled.
My code is adapted from Google's own samples so a lot of this may look quite familiar. I've trimmed down the sign-in handling since that appears to be working fine.
- (instancetype) init
{
self = [super init];
if (!self) return nil;
[GIDSignIn sharedInstance].clientID = (NSString *)kGoogleClientId;
//kGoogleClientId is the ID from the developer console.
[GIDSignIn sharedInstance].delegate = self;
[GIDSignIn sharedInstance].scopes = #[kGTLRAuthScopeDriveAppdata];
return self;
}
//GIDSignInDelegate method
- (void) signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error
{
authenticatedUser = user; //authenticatedUser is an instance variable
NSLog(#"Signed in to Google Drive with user %#", user.profile.name);
[delegate GoogleDriveDidSignIn:self];
}
- (GTLRDriveService *) driveService
{
static GTLRDriveService *service;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{
service = [[GTLRDriveService alloc] init];
service.APIKey = (NSString *)kGoogleApiKey;
//kGoogleApiKey matches the developer console too. It has unrestricted API access and is tied to my bundle ID
service.APIKeyRestrictionBundleID = [[NSBundle mainBundle] bundleIdentifier];
service.shouldFetchNextPages = YES;
service.retryEnabled = YES;
});
service.authorizer = authenticatedUser.authentication.fetcherAuthorizer;
//authenticatedUser is an instance variable which stores the user information returned by
//GIDSignIn when the user signs in
return service;
}
- (void) createFolderNamed:(NSString *)folderName completionHandler:(void(^)(NSString *foldername, NSString *newFolderId))completionHandler
{
GoogleDriveHandler * __weak weakself = self;
GTLRDriveService *service = [self driveService];
GTLRDrive_File *folder = [GTLRDrive_File object];
folder.name = folderName;
folder.mimeType = (NSString *)kMimeType_GoogleDriveFolder;
folder.parents = #[#"appDataFolder"];
GTLRDriveQuery_FilesCreate *query = [GTLRDriveQuery_FilesCreate queryWithObject:folder uploadParameters:nil];
[service executeQuery:query completionHandler:^(GTLRServiceTicket * _Nonnull callbackTicket, id _Nullable object, NSError * _Nullable callbackError)
{
if (callbackError)
{
NSLog(#"-createFolderNamed: callbackError: %#", callbackError.localizedDescription);
}
else
{
GTLRDrive_File *createdFolder = (GTLRDrive_File *)object;
if ( [createdFolder.mimeType isEqualToString:(NSString *)kMimeType_GoogleDriveFolder] )
{
NSLog(#"Google Drive created folder named \"%#\" with identifier \"%#\" and mime-type \"%#\"", createdFolder.name, createdFolder.identifier, createdFolder.mimeType);
}
else
{
NSLog(#"Error : Attempted to create folder, but Google Drive created item named \"%#\" with identifier \"%#\" and mime-type \"%#\"", createdFolder.name, createdFolder.identifier, createdFolder.mimeType);
}
}
}];
}
- (void) writeFileAtUrl:(NSURL *)source toFolderWithId:(NSString *)folderId completionHandler:(void(^)(NSString *filename, NSString *newFileId))completionHandler
{
GoogleDriveHandler * __weak weakself = self;
GTLRDriveService *service = [self driveService];
GTLRDrive_File *file = [GTLRDrive_File object];
file.name = source.lastPathComponent;
file.mimeType = #"binary/octet-stream";
file.parents = #[folderId];
file.spaces = #[#"appDataFolder"];
GTLRUploadParameters *parameters = [GTLRUploadParameters uploadParametersWithFileURL:source MIMEType:#"binary/octet-stream"];
parameters.shouldUploadWithSingleRequest = YES;
GTLRDriveQuery_FilesCreate *query = [GTLRDriveQuery_FilesCreate queryWithObject:file uploadParameters:parameters];
query.fields = #"id";
[service executeQuery:query completionHandler:^(GTLRServiceTicket * _Nonnull callbackTicket, id _Nullable object, NSError * _Nullable callbackError)
{
if (callbackTicket.statusCode == 200)
{
GTLRDrive_File *createdFile = (GTLRDrive_File *)object;
NSLog(#"Wrote file %# in Google Drive folder %#", createdFile.name, folderId);
if (completionHandler) completionHandler(createdFile.name, createdFile.identifier);
}
else
{
NSLog(#"-writeFileAtUrl:toFolderWithId:completionHandler: status code = %li : callbackError: %#", callbackTicket.statusCode, callbackError.localizedDescription);
}
}];
}
As an example, I've tried doing this after GIDSignIn logs in:
NSURL *sampleFile = [[NSBundle mainBundle] URLForResource:#"AValidTestFile" withExtension:#"png"];
if (sampleFile)
{
[self writeFileAtUrl:sampleFile toFolderWithId:#"appDataFolder" completionHandler:^(NSString *filename, NSString *newFileId)
{
NSLog(#"Uploaded file %# with ID %#", filename, newFileId);
}];
}
I still just get a 403 error.
At this point, I've read a huge number of tutorials, blog posts and forum threads in several different programming languages. I've gone over my own code several times and added an insane number of logging statements to double check everything, but I can't work out how I can have permission to create folders, but not to put files in them.
Some time later...
If you go through the credential wizard in the Google Console (rather than just selecting an iOS credential because you're creating an iOS app), you get a message which says "Application data cannot be accessed securely from iOS. Please consider selecting another platform" and it refuses to create a credential for you. Is it possible that this just doesn't work, despite the SDK having the necessary constants?
For those who follow after me, I think I've concluded that using the appDataFolder in iOS just doesn't work.
Having switched to using a folder in the Drive space, I've also found that the -uploadParametersWithFileURL:MIMEType: method of GTLRUploadParameters doesn't work. When I use that I get a file called 'Untitled' (containing the file metadata I set in my GTLRDrive_File object) in the root of the drive. As soon as I switched to -uploadParametersWithData:MIMEType: I got the correct file in the correct place.
I suppose the lesson so far is that if something isn't working, assume it’s the SDK.

MacOS VPN Error NO Shared Secret was Provided Objective-C

I Face a big problem,I made a VPN for IOS Devices,it is on the appstore,it is working nicely,However i face problem with macos application,I use same codes.Same codes work for IOS Devices however they dont work for MacOS application.
The Problem says ;
No VPN shared secret was provided. Verify your settings and try reconnecting.
When i try to connect to vpn.
Code that i use for connect vpn
[[NEVPNManager sharedManager] loadFromPreferencesWithCompletionHandler: ^(NSError *error) {
NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
p.serverAddress =ipAddress;
p.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
NSLog(#"vpn connecting to ip %#",ipAddress);
p.username=#"vpnuser";
p.sharedSecretReference = [KeyChainHelper load:sharedSecret];
p.passwordReference = [KeyChainHelper load:password];
// NSData * data = [self searchKeychainCopyMatching:sharedSecret];
p.disconnectOnSleep = NO;
[NEVPNManager sharedManager].protocolConfiguration=p;
NSMutableArray *rules = [[NSMutableArray alloc] init];
NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new];
[rules addObject:connectRule];
[[NEVPNManager sharedManager] setOnDemandRules:rules];
[[NEVPNManager sharedManager] setLocalizedDescription:#"VPN Fire"];
[[NEVPNManager sharedManager] setEnabled:YES];
[[NEVPNManager sharedManager] saveToPreferencesWithCompletionHandler: ^(NSError *error) {
NSLog(#"Save VPN to preference complete");
if (error) {
NSLog(#"Save error: %#", error);
}
NSError *startError;
[[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
delayStart(5)
NSLog(#"%#",[NEVPNManager sharedManager]);
NSLog(#"%#",[NEVPNManager sharedManager]);
delayEnd
if(startError) {
NSLog(#"Start error: %#", startError.localizedDescription);
}
}];
}];
Here i put some screenshoots;
Looks like KeyChainHelper is a wrapper for accessing the keychain. It isn't clear exactly what the KeyChainHelper::load() method returns, but my guess is either a standard reference or data.
NEVPNProtocolIPSec::sharedSecretReference is defined as:
A persistent keychain reference to a keychain item containing the IKE shared secret.
You need to create a keychain item of type kSecClassGenericPassword to use as the shared secret, and provide a persistent reference to the keychain item. A persistent reference is critical here, as it can be stored to disk or passed between processes, which is required for NEVPNManager to access the shared secret and connect your VPN. Likewise for passwordReference.
You will need to use SecItemCopyMatching with return type kSecReturnPersistentRef to achieve this. Your KeyChainHelper class may provide a method to return the persistent reference.

how to check No Fingerprints added for Touch ID in iOS

I am integrating Touch ID access in one of my app. I have successfully integrated it. Here is that code:
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.75 * NSEC_PER_SEC), highPriorityQueue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
LAContext *context = [[LAContext alloc] init];
isTouchExists = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
if (isTouchExists) {
NSString * keychainItemIdentifier;
NSString * keychainItemServiceName;
keychainItemIdentifier = #"fingerprintKeychainEntry";
keychainItemServiceName = [[NSBundle mainBundle] bundleIdentifier];
NSData * pwData = [#"the password itself does not matter" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary * attributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
(__bridge id)(kSecClassGenericPassword), kSecClass,
keychainItemIdentifier, kSecAttrAccount,
keychainItemServiceName, kSecAttrService, nil];
CFErrorRef accessControlError = NULL;
SecAccessControlRef accessControlRef = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAccessControlUserPresence,
&accessControlError);
if (accessControlRef == NULL || accessControlError != NULL)
{
NSLog(#"Cannot create SecAccessControlRef to store a password with identifier “%#” in the key chain: %#.", keychainItemIdentifier, accessControlError);
}
attributes[(__bridge id)kSecAttrAccessControl] = (__bridge id)accessControlRef;
attributes[(__bridge id)kSecUseNoAuthenticationUI] = #YES;
attributes[(__bridge id)kSecValueData] = pwData;
CFTypeRef result;
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)attributes, &result);
if (osStatus != noErr)
{
NSError * error = [[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
NSLog(#"Adding generic password with identifier “%#” to keychain failed with OSError %d: %#.", keychainItemIdentifier, (int)osStatus, error);
}
//other my code for success
}
});
});
Now, If I remove all the fingerprints from settings in iPhone, This code will work and ask for passcode. So My question is:
how can I come to know that there is no any fingerprints added for Touch ID?
I don't want to show iOS device passcode screen as I have already built passcode screen for my app security. So is there any option to check device have atleast one fingerprint available for Touch ID access?
Thanks in advance.
======== EDIT 1 ===========
It is working on my side also. The issue is I need to check it each time when I am asking for Touch ID. I need to fetch status in viewWillAppear or in applicationDidBecomeActive each time whenever I want to use Touch ID access in app, as I am removing fingers run time, it may not reflecting in my code so I need to fetch each time.
canEvaluatePolicy:error: will be Error : LAErrorTouchIDNotEnrolled
Authentication could not start because Touch ID has no enrolled
fingers.
APPLE DOC Ref.
Try:
LAContext *context = [[LAContext alloc] init];
NSError *error;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"My Reason" reply:^(BOOL success, NSError * _Nullable error) {
}];
}else{
if (error.code == LAErrorTouchIDNotEnrolled) {
NSLog(#"Error: %#", error.localizedDescription);
}
}
If there are no fingerprints registered, canEvaluatePolicy should return false.
Source : https://developer.apple.com/documentation/localauthentication/lacontext/1514149-canevaluatepolicy?language=objc

NEVPNManager instance is showing nil

I had created network manager by using the below code. but as soon the first line of call is run the manager is being nil so any other methods are getting not effected. Can Anyone let me know that what wrong thing I had done ?
NEVPNManager *manager = [NEVPNManager sharedManager];
NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
p.username = #"MYUSERNAME";
p.passwordReference = [#"MYPW" dataUsingEncoding:NSUTF8StringEncoding];
p.serverAddress = #"[NetworkIP]";
p.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
p.sharedSecretReference = [#"SharedSecretHashCode" dataUsingEncoding:NSUTF8StringEncoding];
[manager setProtocol:p];
[manager setOnDemandEnabled:NO];
[manager setLocalizedDescription:#"VIT VPN"];
NSArray *array = [NSArray new];
[manager setOnDemandRules: array];
NSLog(#"Connection desciption: %#", manager.localizedDescription);
NSLog(#"VPN status: %li", (long)manager.connection.status);
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if(error) {
NSLog(#"Save error: %#", error);
}
}];
"manager" is being nil . please help. thanks in advance.
I found Solution for my question for the NEVPNManager instances.
When using this to the app developer must need its related certificates in which The APPID has has enabled the VPN Configuration & controls. and this app must need to run in the device.
So the NEVPNManager will not be nil and it can be adapt from the shared instance of the device. (also this is from NetworkExtension.framework which is available for iOS8 & higher devices)
May be this can help anyone who is having the same issue.

iOS Authenticate Azure Active Directory & get calendar events from office 365 exchange

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
}];
}
}];
}

Resources