Mobile SDKを使うとSalesforceにアクセスするモバイルアプリケーションが簡単に作れそうですが、単一の組織にログインするのが前提なため要件に合わず、今回はMobile SDKを使わずに作ってみましたので基本事項をまとめておきます(XcodeでのiPhoneアプリ開発の知識があるの前提です)。
Salesforceの認証(OAuth2.0)については、こちらをまず読んでおくと分かりやすいです。
https://wiki.developerforce.com/page/JP:Digging_Deeper_into_OAuth_2.0_at_Salesforce.com
接続アプリケーションの登録
開発用のSalesforce組織にログインし、「設定>アプリケーション」から接続アプリケーションを登録します。
ここで発行される「コンシューマ鍵」(=client_id)が認証の実装に必要です。
StoryboardでUIの大枠を作る
まずは簡単に、認証してユーザ情報を表示するだけのアプリを作ります。
新規プロジェクトで、Master-Detail Applicationをひな形に、
- Master ViewにはSign In用のボタン代わりのセルと、取得したユーザ情報を表示する用のセルをstatic cellとして置きました。
- Detail Viewには認証のページを表示する為のUIWebViewを置きました。
OAuthでの認証
UIWebViewの初期表示として、認証画面に飛ばします。
クライアントアプリケーション用の認証方式、User-Agent Flowを使うため、response_type=token
を指定します。
- (void)viewDidLoad
{
[super viewDidLoad];
// OAuthの認証画面を初期表示(User-Agent Flow)
NSString *authorizeURL = @"https://login.salesforce.com/services/oauth2/authorize?response_type=token&client_id=3MVG9I1kFE5Iul2Cbahhzus9WVFyGSQGrQ3gTYeMzkJGbA_43hAg0fb57FTxZkWASpInGI4lSg3gpzlL4WErb&redirect_uri=test%3A%2F%2Fcallback&display=touch";
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:authorizeURL]];
_webview.delegate = self;
[_webview loadRequest:req];
}
認証が終わった時のtest://callback
の処理をhookし、パラメータで返ってくる認証情報をバラします。
取得したaccess_tokenを使用して、idサービスでユーザの情報を取得します。
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *absoluteURL = request.URL.absoluteString;
// callback先以外の場合は通常のページ遷移
if ([absoluteURL rangeOfString:@"test://callback"].location == NSNotFound) return YES;
// #以降のパラメータをバラす
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:@"(\\w+)=([^&]+)" options:0 error:nil];
NSArray *matches = [re matchesInString:absoluteURL options:0 range:NSMakeRange(0, absoluteURL.length)];
NSMutableDictionary *authInfo = @{}.mutableCopy;
for (NSTextCheckingResult *r in matches) {
NSString *k = [absoluteURL substringWithRange:[r rangeAtIndex:1]];
NSString *v = [absoluteURL substringWithRange:[r rangeAtIndex:2]];
authInfo[k] = [v stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
}
// User-Agent Flowなのでaccess_tokenが直接返ってくる
NSString *accessToken = authInfo[@"access_token"];
// 取得したaccess_tokenを使ってidサービスに接続。ユーザ情報を取得する
// access_tokenをAuthorizationヘッダへ
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPAdditionalHeaders = @{@"Authorization": [NSString stringWithFormat:@"Bearer %@", accessToken]};
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
// idサービスのURLは'id'パラメータで返ってくる
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:authInfo[@"id"]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// 取得したユーザ情報をMain Viewに渡して画面戻る
NSDictionary *idInfo = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// メインスレッドで実行
[self performSegueWithIdentifier:@"oauthSuccess" sender:@{@"authInfo":authInfo, @"idInfo":idInfo}];
}];
}];
[task resume];
return NO;
}
エラー処理等は特に入れてない基本部分のみですが、これでaccess_tokenを取得し、APIの呼び出しができました。