Skip to content

Instantly share code, notes, and snippets.

@ChrisRisner
Last active December 16, 2015 07:38
Show Gist options
  • Save ChrisRisner/5399676 to your computer and use it in GitHub Desktop.
Save ChrisRisner/5399676 to your computer and use it in GitHub Desktop.
iOS Authentication Demo
-(NSMutableURLRequest *)addQueryStringParamToRequest:(NSMutableURLRequest *)request {
NSMutableString *absoluteURLString = [[[request URL] absoluteString] mutableCopy];
NSString *newQuery = @"?bypass=true";
[absoluteURLString appendString:newQuery];
[request setURL:[NSURL URLWithString:absoluteURLString]];
return request;
}
-(AuthService *) init {
self = [super init];
if (self) {
// Initialize the Mobile Service client with your URL and key
self.client = [MSClient clientWithApplicationURLString:@"https://MyMobileServiceUrl.azure-mobile.net/"
applicationKey:@"MyMobileServiceApplicationKey"];
self.client = [self.client clientWithFilter:self];
[self loadAuthInfo];
self.table = [_client tableWithName:@"AuthData"];
self.accountsTable = [_client tableWithName:@"Accounts"];
}
return self;
}
- (void) filterResponse: (NSHTTPURLResponse *) response
forData: (NSData *) data
withError: (NSError *) error
forRequest:(NSURLRequest *) request
onNext:(MSFilterNextBlock) onNext
onResponse: (MSFilterResponseBlock) onResponse
{
if (response.statusCode == 401) {
[self killAuthInfo];
//we're forcing custom auth to relogin from the root for now
if (self.shouldRetryAuth && ![self.authProvider isEqualToString:@"Custom"]) {
// show the login dialog
[self.client loginWithProvider:self.authProvider controller:[[[[UIApplication sharedApplication] delegate] window] rootViewController] animated:YES completion:^(MSUser *user, NSError *error) {
if (error && error.code == -9001) {
// user cancelled authentication
//Log them out here too
[self triggerLogout];
return;
}
[self saveAuthInfo];
NSMutableURLRequest *newRequest = [request mutableCopy];
//Update the zumo auth token header in the request
[newRequest setValue:self.client.currentUser.mobileServiceAuthenticationToken forHTTPHeaderField:@"X-ZUMO-AUTH"];
//Add our bypass query string parameter so this request doesn't get a 401
newRequest = [self addQueryStringParamToRequest:newRequest];
onNext(newRequest, ^(NSHTTPURLResponse *innerResponse, NSData *innerData, NSError *innerError){
[self filterResponse:innerResponse
forData:innerData
withError:innerError
forRequest:request
onNext:onNext
onResponse:onResponse];
});
}];
} else {
[self triggerLogout];
}
}
else {
onResponse(response, data, error);
}
}
- (void) handleRequest:(NSURLRequest *)request
next:(MSFilterNextBlock)onNext
response:(MSFilterResponseBlock)onResponse {
onNext(request, ^(NSHTTPURLResponse *response, NSData *data, NSError *error){
[self filterResponse:response
forData:data
withError:error
forRequest:request
onNext:onNext
onResponse:onResponse];
});
}
- (void)killAuthInfo {
[KeychainItemWrapper deleteItemFromKeychainWithIdentifier:@"userid"];
[KeychainItemWrapper deleteItemFromKeychainWithIdentifier:@"token"];
for (NSHTTPCookie *value in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:value];
}
[self.client logout];
}
- (void)loadAuthInfo {
NSString *userid = [KeychainItemWrapper keychainStringFromMatchingIdentifier:@"userid"];
if (userid) {
NSLog(@"userid: %@", userid);
self.client.currentUser = [[MSUser alloc] initWithUserId:userid];
self.client.currentUser.mobileServiceAuthenticationToken = [KeychainItemWrapper keychainStringFromMatchingIdentifier:@"token"];
}
}
- (void) loginAccount:(NSDictionary *) item
completion:(CompletionWithStringBlock) completion {
NSDictionary *params = @{ @"login" : @"true"};
[self.accountsTable insert:item parameters:params completion:^(NSDictionary *item, NSError *error) {
[self logErrorIfNotNil:error];
if (error) {
completion([error localizedDescription]);
return;
} else {
MSUser *user = [[MSUser alloc] initWithUserId:[item valueForKey:@"userId"]];
user.mobileServiceAuthenticationToken = [item valueForKey:@"token"];
self.client.currentUser = user;
[self saveAuthInfo];
completion(@"SUCCESS");
}
}];
}
-(void) loginWithProvider:(NSString *)provider
{
//Save the provider in case we need to reauthorize them
self.authService.authProvider = provider;
MSLoginController *controller =
[self.authService.client
loginViewControllerWithProvider:provider
completion:^(MSUser *user, NSError *error) {
if (error) {
NSLog(@"Authentication Error: %@", error);
// Note that error.code == -1503 indicates
// that the user cancelled the dialog
} else {
[self.authService saveAuthInfo];
[self performSegueWithIdentifier:@"loggedInSegue" sender:self];
}
[self dismissViewControllerAnimated:YES completion:nil];
}];
[self presentViewController:controller animated:YES completion:nil];
}
-(IBAction)logout:(UIStoryboardSegue *)segue {
[self.authService killAuthInfo];
}
- (void) registerAccount:(NSDictionary *) item
completion:(CompletionWithStringBlock) completion {
[self.accountsTable insert:item completion:^(NSDictionary *item, NSError *error) {
[self logErrorIfNotNil:error];
if (error) {
completion([error localizedDescription]);
return;
} else {
MSUser *user = [[MSUser alloc] initWithUserId:[item valueForKey:@"userId"]];
user.mobileServiceAuthenticationToken = [item valueForKey:@"token"];
self.client.currentUser = user;
[self saveAuthInfo];
completion(@"SUCCESS");
}
}];
}
- (void)saveAuthInfo {
[KeychainItemWrapper createKeychainValue:self.client.currentUser.userId forIdentifier:@"userid"];
[KeychainItemWrapper createKeychainValue:self.client.currentUser.mobileServiceAuthenticationToken forIdentifier:@"token"];
}
- (IBAction)tappedLogin:(id)sender {
NSDictionary *item = @{ @"username" : self.txtUsername.text,
@"password" : self.txtPassword.text
};
[self.authService loginAccount:item completion:^(NSString *string) {
if ([string isEqualToString:@"SUCCESS"]) {
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
[self performSegueWithIdentifier:@"customAuthSegue" sender:self];
} else {
self.lblInfo.text = string;
}
}];
}
- (IBAction)tappedRegister:(id)sender {
if ([self.txtUsername.text isEqualToString:@""] || [self.txtEmail.text isEqualToString:@""] || [self.txtPassword.text isEqualToString:@""]) {
self.lblInfo.text = @"You must enter all fields.";
return;
} else if (![self.txtPassword.text isEqualToString:self.txtConfirm.text]) {
self.lblInfo.text = @"The passwords you've entered do not match.";
return;
}
self.lblInfo.text = @"";
NSDictionary *item = @{ @"username" : self.txtUsername.text,
@"password" : self.txtPassword.text,
@"email" : self.txtEmail.text };
[self.authService registerAccount:item completion:^(NSString *string) {
if ([string isEqualToString:@"SUCCESS"]) {
[self performSegueWithIdentifier:@"customAuthSegue" sender:self];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
} else {
self.lblInfo.text = string;
}
}];
}
- (void) testForced401:(BOOL)shouldRetry withCompletion:(CompletionWithStringBlock) completion {
MSTable *badAuthTable = [_client tableWithName:@"BadAuth"];
NSDictionary *item = @{ @"data" : @"data"};
self.shouldRetryAuth = shouldRetry;
[badAuthTable insert:item completion:^(NSDictionary *item, NSError *error) {
[self logErrorIfNotNil:error];
completion(@"Retried auth success");
}];
}
-(void)triggerLogout {
[self killAuthInfo];
UIViewController *rootVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
UINavigationController *navVC = (UINavigationController *)rootVC;
UIViewController *topVC = navVC.topViewController;
[topVC performSegueWithIdentifier:@"logoutSegue" sender:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.authService = [AuthService getInstance];
if (self.authService.client.currentUser.userId) {
[self performSegueWithIdentifier:@"loggedInSegue" sender:nil];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment