2016-02-24 16:56:39 +01:00

181 lines
4.2 KiB
Objective-C

//
// XMPPXOAuth2Google.m
// Off the Record
//
// Created by David Chiles on 9/13/13.
// Copyright (c) 2013 Chris Ballinger. All rights reserved.
//
#import "XMPPXOAuth2Google.h"
#import "XMPP.h"
#import "XMPPLogging.h"
#import "XMPPInternal.h"
#import "NSData+XMPP.h"
#import <objc/runtime.h>
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
// Log levels: off, error, warn, info, verbose
#if DEBUG
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
#else
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#endif
static NSString *const XMPPGoogleTalkHostName = @"talk.google.com";
@interface XMPPXOAuth2Google ()
{
#if __has_feature(objc_arc_weak)
__weak XMPPStream *xmppStream;
#else
__unsafe_unretained XMPPStream *xmppStream;
#endif
//BOOL awaitingChallenge;
//NSString *appId;
NSString *accessToken;
//NSString *nonce;
//NSString *method;
}
@end
@implementation XMPPXOAuth2Google
+ (NSString *)mechanismName
{
return @"X-OAUTH2";
}
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)password
{
if ((self = [super init]))
{
xmppStream = stream;
xmppStream.hostName = XMPPGoogleTalkHostName;
}
return self;
}
-(id)initWithStream:(XMPPStream *)stream accessToken:(NSString *)inAccessToken
{
if (self = [super init]) {
xmppStream = stream;
accessToken = inAccessToken;
}
return self;
}
- (BOOL)start:(NSError **)errPtr
{
if (!accessToken)
{
NSString *errMsg = @"Missing facebook accessToken.";
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
NSError *err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
if (errPtr) *errPtr = err;
return NO;
}
XMPPLogTrace();
// From RFC 4616 - PLAIN SASL Mechanism:
// [authzid] UTF8NUL authcid UTF8NUL passwd
//
// authzid: authorization identity
// authcid: authentication identity (username)
// passwd : password for authcid
NSString *username = [xmppStream.myJID user];
NSString *payload = [NSString stringWithFormat:@"\0%@\0%@", username, accessToken];
NSString *base64 = [[payload dataUsingEncoding:NSUTF8StringEncoding] xmpp_base64Encoded];
// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">Base-64-Info</auth>
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
[auth addAttributeWithName:@"mechanism" stringValue:@"X-OAUTH2"];
[auth addAttributeWithName:@"auth:service" stringValue:@"oauth2"];
[auth addAttributeWithName:@"xmlns:auth" stringValue:@"http://www.google.com/talk/protocol/auth"];
[auth setStringValue:base64];
[xmppStream sendAuthElement:auth];
return YES;
}
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
{
XMPPLogTrace();
// We're expecting a success response.
// If we get anything else we can safely assume it's the equivalent of a failure response.
if ([[authResponse name] isEqualToString:@"success"])
{
return XMPP_AUTH_SUCCESS;
}
else
{
return XMPP_AUTH_FAIL;
}
}
@end
@implementation XMPPStream (XMPPXOAuth2Google)
- (BOOL)supportsXOAuth2GoogleAuthentication
{
return [self supportsAuthenticationMechanism:[XMPPXOAuth2Google mechanismName]];
}
- (BOOL)authenticateWithGoogleAccessToken:(NSString *)accessToken error:(NSError **)errPtr
{
XMPPLogTrace();
__block BOOL result = YES;
__block NSError *err = nil;
dispatch_block_t block = ^{ @autoreleasepool {
if ([self supportsXOAuth2GoogleAuthentication])
{
XMPPXOAuth2Google * googleAuth = [[XMPPXOAuth2Google alloc] initWithStream:self
accessToken:accessToken];
result = [self authenticate:googleAuth error:&err];
}
else
{
NSString *errMsg = @"The server does not support X-OATH2-GOOGLE authentication.";
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
result = NO;
}
}};
if (dispatch_get_specific(self.xmppQueueTag))
block();
else
dispatch_sync(self.xmppQueue, block);
if (errPtr)
*errPtr = err;
return result;
}
@end