Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fixes & improvements #1

Merged
merged 4 commits into from

2 participants

Jens Alfke Stefan Arentz
Jens Alfke

No description provided.

snej added some commits
Jens Alfke snej Make it work again with the current version of BrowserID
On advice of Sean McArthur:
* Changed WebView's URL.
* Changed API function called when the page loads.
cdd6fdb
Jens Alfke snej Make user's email address available to caller
Extract email address from assertion,
and store it in controller's .emailAddress property.
83cc9e8
Jens Alfke snej Code cleanup and header-doc comments. a2a8270
Jens Alfke snej Oops, converted tabs to spaces a5dc772
Stefan Arentz
Collaborator

Thank you Jens. These improvements are great.

Stefan Arentz st3fan merged commit c41c37e into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 6, 2013
  1. Jens Alfke

    Make it work again with the current version of BrowserID

    snej authored
    On advice of Sean McArthur:
    * Changed WebView's URL.
    * Changed API function called when the page loads.
  2. Jens Alfke

    Make user's email address available to caller

    snej authored
    Extract email address from assertion,
    and store it in controller's .emailAddress property.
Commits on Jan 7, 2013
  1. Jens Alfke
  2. Jens Alfke

    Oops, converted tabs to spaces

    snej authored
This page is out of date. Refresh to see the latest.
46 Sources/BrowserIDViewController.h
View
@@ -4,13 +4,34 @@
@class BrowserIDViewController;
+/** Delegate of a BrowserIDViewController. Of the optional methods, didSucceedWithAssertion
+ must be implemented unless the controller's 'verifier' property is set, in which case both
+ didSucceedVerificationWithReceipt and didFailVerificationWithError must be implemented. */
@protocol BrowserIDViewControllerDelegate <NSObject>
+
+/** Sent if the user presses the Cancel button on the BrowserID window. */
- (void) browserIDViewControllerDidCancel: (BrowserIDViewController*) browserIDViewController;
-- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController didFailWithReason: (NSString*) reason;
+
+/** Sent if the authentication process fails. Currently the reason will just be @"". */
+- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController
+ didFailWithReason: (NSString*) reason;
@optional
-- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController didSucceedWithAssertion: (NSString*) assertion;
-- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController didSucceedVerificationWithReceipt: (NSDictionary*) receipt;
-- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController didFailVerificationWithError: (NSError*) error;
+/** Sent after authentication was successful. The assertion will be a long opaque string that
+ should be sent to the origin site's BrowserID authentication API. */
+- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController
+ didSucceedWithAssertion: (NSString*) assertion;
+
+/** Sent after authentication and server-side verification are successful, _only_ if the
+ controller's 'verifier' property is set to a server-side verifier URL.
+ The 'receipt' parameter is the verifier response as decoded from JSON. */
+- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController
+ didSucceedVerificationWithReceipt: (NSDictionary*) receipt;
+
+/** Sent if server-side verification fails, _only_ if the controller's 'verifier' property is set
+ to a server-side verifier URL. */
+- (void) browserIDViewController: (BrowserIDViewController*) browserIDViewController
+ didFailVerificationWithError: (NSError*) error;
+
@end
@interface BrowserIDViewController : UIViewController <UIWebViewDelegate> {
@@ -18,8 +39,23 @@
@property (nonatomic,strong) IBOutlet UIWebView* webView;
+/** The object that will be informed about success or failure. Required. */
@property (nonatomic,weak) id<BrowserIDViewControllerDelegate> delegate;
-@property (nonatomic,strong) NSString* origin;
+
+/** The URL of the site the user is logging into (i.e. the site you will send the assertion to).
+ Required. */
+@property (nonatomic,strong) NSURL* origin;
+
+/** An optional URL of a verification service provided by your applicatin's server-side counterpart.
+ If this property is set, an assertion will be sent to this URL as the body of a POST request,
+ and the response relayed to the delegate via its verification-related methods. */
@property (nonatomic,strong) NSURL* verifier;
+/** After a successful login, this property will be set to the email address the user entered. */
+@property (nonatomic,strong) NSString* emailAddress;
+
+/** A convenience method that puts the receiver in a UINavigationController and presents it modally
+ in the given parent controller. */
+- (UINavigationController*) presentModalInController: (UIViewController*)parentController;
+
@end
43 Sources/BrowserIDViewController.js
View
@@ -2,25 +2,30 @@
window.onload = function()
{
- // The origin is setup when this code template is loaded in the native application
- var origin = "%@";
-
- var callbackToCocoa = function(name, value) {
- window.location = "BrowserIDViewController://" + name + "/callback?data=" + value;
- };
+ // The origin is setup when this code template is loaded in the native application
+ var origin = "%@";
+
+ var callbackToCocoa = function(name, value) {
+ window.location = "BrowserIDViewController://" + name + "/callback?data=" + value;
+ };
- var internalGetCallback = function(assertion) {
- if (assertion) {
- callbackToCocoa("assertionReady", assertion);
- } else {
- // Not sure what to do here. I don't think there is a 'reason'?
- callbackToCocoa("assertionFailure", "");
- }
- };
+ var internalGetCallback = function(assertion) {
+ if (!assertion) {
+ // Not sure what to do here. I don't think there is a 'reason'?
+ callbackToCocoa("assertionFailure", "");
+ return;
+ }
- var internalSetPersistentCallback = function() {
- BrowserID.internal.get(origin, internalGetCallback, {silent: false})
- };
-
- BrowserID.internal.setPersistent(origin, internalSetPersistentCallback);
+ // Parse the assertion to extract the email address the user entered:
+ var parts = assertion.split(".");
+ var signature = JSON.parse(window.atob(parts[1]));
+ var email = signature.principal.email;
+
+ callbackToCocoa("assertionReady",
+ encodeURIComponent(assertion) + "&email=" + encodeURIComponent(email));
+ };
+
+ // Start the login process:
+ var options = {};
+ BrowserID.internal.get(origin, internalGetCallback, options);
};
106 Sources/BrowserIDViewController.m
View
@@ -6,7 +6,7 @@
#define __has_feature(x) 0
#endif
-static NSString* kBrowserIDSignInURL = @"https://www.browserid.org/sign_in";
+static NSString* const kBrowserIDSignInURL = @"https://login.persona.org/sign_in#NATIVE";
@implementation BrowserIDViewController
@@ -14,13 +14,27 @@ @implementation BrowserIDViewController
@synthesize delegate = _delegate;
@synthesize origin = _origin;
@synthesize verifier = _verifier;
+@synthesize emailAddress = _emailAddress;
+
+- (UINavigationController*) presentModalInController: (UIViewController*)parentController {
+ UINavigationController* navController = [[UINavigationController alloc]
+ initWithRootViewController: self];
+ if (navController == nil)
+ return nil;
+
+ if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone) {
+ navController.modalPresentationStyle = UIModalPresentationFormSheet;
+ }
+ [parentController presentViewController: navController animated: YES completion: nil];
+ return navController;
+}
- (void) viewDidLoad
{
[super viewDidLoad];
-
- self.title = @"BrowserID";
-
+
+ self.title = NSLocalizedString(@"Log In With Persona", @"BrowserID login window title");
+
UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc] initWithTitle: @"Cancel"
style: UIBarButtonItemStylePlain target: self action: @selector(cancel)];
#if !__has_feature(objc_arc)
@@ -31,18 +45,15 @@ - (void) viewDidLoad
_webView.delegate = self;
- // Insert the code that will setup and handle the BrowserID callback.
+ // Insert the code that will setup and handle the BrowserID callback.
- NSString* injectedCodePath = [[NSBundle mainBundle] pathForResource: @"BrowserIDViewController" ofType: @"js"];
- NSString* injectedCodeTemplate = [NSString stringWithContentsOfFile: injectedCodePath encoding:NSUTF8StringEncoding error: nil];
- if (injectedCodeTemplate == nil) {
- NSLog(@"Could not load BrowserIDViewController.js");
- return;
- }
-
- NSString* injectedCode = [NSString stringWithFormat: injectedCodeTemplate, _origin];
+ NSString* injectedCodePath = [[NSBundle mainBundle] pathForResource: @"BrowserIDViewController" ofType: @"js"];
+ NSString* injectedCodeTemplate = [NSString stringWithContentsOfFile: injectedCodePath encoding:NSUTF8StringEncoding error: nil];
+ NSAssert(injectedCodeTemplate != nil, @"Could not load BrowserIDViewController.js");
- [_webView stringByEvaluatingJavaScriptFromString: injectedCode];
+ NSString* injectedCode = [NSString stringWithFormat: injectedCodeTemplate, _origin.absoluteString];
+
+ [_webView stringByEvaluatingJavaScriptFromString: injectedCode];
}
- (void) viewWillAppear:(BOOL)animated
@@ -78,7 +89,7 @@ - (void) verifyAssertion: (NSString*) assertion
{
// POST the assertion to the verification endpoint. Then report back to our delegate about the
// results.
-
+
id verifyCompletionHandler = ^(NSHTTPURLResponse* response, NSData* data, NSError* error)
{
if (error) {
@@ -93,7 +104,7 @@ - (void) verifyAssertion: (NSString*) assertion
}
}
};
-
+
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL: self.verifier cachePolicy: NSURLCacheStorageAllowed timeoutInterval: 5.0];
#if !__has_feature(objc_arc)
[request autorelease];
@@ -102,39 +113,52 @@ - (void) verifyAssertion: (NSString*) assertion
[request setHTTPMethod: @"POST"];
[request setHTTPBody: [assertion dataUsingEncoding: NSUTF8StringEncoding]];
[request setValue: @"text/plain" forHTTPHeaderField: @"content-type"];
-
+
[NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue]
completionHandler: verifyCompletionHandler];
}
- (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
- NSURL* url = [request URL];
-
- // The JavaScript side (the code injected in viewDidLoad will make callbacks to this native code by requesting
- // a BrowserIDViewController://callbackname/callback?data=foo style URL. So we capture those here and relay
- // them to our delegate.
-
- if ([[[url scheme] lowercaseString] isEqualToString: @"browseridviewcontroller"])
- {
- if ([[url host] isEqualToString: @"assertionReady"]) {
- NSString* assertion = [[url query] substringFromIndex: [@"data=" length]];
+ NSURL* url = [request URL];
+
+ // The JavaScript side (the code injected in viewDidLoad will make callbacks to this native code by requesting
+ // a BrowserIDViewController://callbackname/callback?data=foo style URL. So we capture those here and relay
+ // them to our delegate.
+
+ if ([[[url scheme] lowercaseString] isEqualToString: @"browseridviewcontroller"])
+ {
+ NSString* message = url.host;
+ NSString* param = [[url query] substringFromIndex: [@"data=" length]];
+ NSLog(@"MESSAGE '%@', param '%@'", message, param);
+ if ([message isEqualToString: @"assertionReady"]) {
+ NSRange separator = [param rangeOfString: @"&email="];
+ if (separator.length > 0) {
+ NSString* email = [param substringFromIndex: NSMaxRange(separator)];
+ param = [param substringToIndex: separator.location];
+ self.emailAddress = [email stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
+ }
+ param = [param stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
if (_verifier) {
- [self verifyAssertion: assertion];
+ [self verifyAssertion: param];
} else {
- [_delegate browserIDViewController: self didSucceedWithAssertion: assertion];
+ [_delegate browserIDViewController: self didSucceedWithAssertion: param];
}
- }
-
- else if ([[url host] isEqualToString: @"assertionFailure"]) {
- [_delegate browserIDViewController: self didFailWithReason: [[url query] substringFromIndex: [@"data=" length]]];
- }
-
- return NO;
- }
-
+ }
+
+ else if ([message isEqualToString: @"assertionFailure"]) {
+ [_delegate browserIDViewController: self didFailWithReason: param];
+ }
+
+ else if ([message isEqualToString: @"gotEmail"]) {
+ self.emailAddress = param;
+ }
+
+ return NO;
+ }
+
// If the user clicked on a link that escapes the browserid dialog, then we open it in Safari
-
+
else if ([[[url scheme] lowercaseString] isEqualToString: @"http"] || [[[url scheme] lowercaseString] isEqualToString: @"https"])
{
if ([[url absoluteString] isEqualToString: kBrowserIDSignInURL] == NO)
@@ -143,8 +167,8 @@ - (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *
return NO;
}
}
-
- return YES;
+
+ return YES;
}
@end
Something went wrong with that request. Please try again.