SlideShare a Scribd company logo
1 of 31
Download to read offline
Hi. I’m Matthew.
@mixonic
httP://madhatted.com
matt.beale@madhatted.com
201 Created
We build õ-age apps with Ember.js. We take
teams from £ to • in no time flat.
http://bit.ly/ember-toronto-edge
authentication
The Goal
•Auth against multiple 3rd party services
•Don’t be reloading the page
•keep the complexity off the server
One page, no reload
Sign In
Auth at Windows Live
Signed In!
OAuth2
•access to resources via tokens
•Several token types
•a different flow for each type
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
resource owner password credentials grant
Do not use this
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Authorization code grant
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
implicit grant
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
implicit grant
browser
Facebook Auth
website server
website
User
Facebook connect
Implicit Grant supported by
•facebook (behind sdk)
•google
•soundcloud
•box.net
•windows live
!
•& more
Building it!
Abstractions
•session manager (controller)
•Oauth adapter
•live, FB specific adapters
•popup manager
6 <div {{bind-attr class=":sign-in-menu isOpen:open:closed"}}>
7 <a class="sign-in-button facebook" href="#" {{action "selectService" "facebook"}}>
8 Sign In with Facebook
9 </a>
10 <a class="sign-in-button windows" href="#" {{action "selectService" "live"}}>
11 Sign In with Windows Live
12 </a>
13 <div class="mobile-show menu-footer subheader">
14 {{link-to 'Leaderboard' 'leaderboard'}}
15 {{link-to 'About' 'map.about'}}
16 </div>
17 </div>
36 signIn: function(service){
37 var session = this.get('session'),
38 route = this;
39 session.open(service)
40 .then(function(){
41 if (route.router.isActive('map')) {
42 route.disconnectOutlet({
43 outlet: 'prompt',
44 parentView: 'map/neighborhood'
45 });
46 }
47 var lastTransition = session.get('afterRedirect');
48 if (lastTransition) {
49 lastTransition.retry();
50 } else if (route.router.isActive('sign_in')) {
51 route.transitionTo('');
52 }
53 }).catch(Ember.Logger.error);
54 },
61 Ember.Application.initializer({
62 name: 'authentication',
63 initialize: function(container, app){
. . .
65 import Session from 'appkit/controllers/session';
66 container.register('session:main', Session);
67 app.inject('controller', 'session', 'session:main');
68 app.inject('route', 'session', 'session:main');
. . .
95 });
inject session
9 open: function(credentials){
10 var session = this;
11 session.set('isAuthenticating', true);
12
13 return this.get('adapter').open(credentials, this)
14 .then(function(user){
15 session.setProperties({
16 isAuthenticated: true,
17 currentUser: user
18 });
19 return user;
20 }).catch(function(err){
21 if (err === 'canceled') {
22 return; // no-op
23 } else {
24 return Ember.RSVP.reject(err);
25 }
26 }).finally(function(){
27 session.set('isAuthenticating', false);
28 });
29 },
opening a session
61 Ember.Application.initializer({
62 name: 'authentication',
63 initialize: function(container, app){
. . .
70 import Auth from 'appkit/models/auth';
71 container.register('session:adapter', Auth);
72 app.inject('session:adapter', 'store', 'store:main');
73 app.inject('session:main', 'adapter', 'session:adapter');
. . .
95 });
Inject Auth adapter
50 // returns a promise that resolves to a user
51 open: function(serviceName){
52 var auth = this;
53 var authService = this.container.lookup('auth:' + serviceName);
54
55 if (!authService) {
56 return Ember.RSVP.reject('unrecognized service auth:' +
57 serviceName);
58 } else {
59 return authService.open()
60 .then( function(serviceData) {
61 // create a session
62 var sessionData = {
63 authData: {
64 name: serviceData.name,
65 id: serviceData.id,
66 accessToken: serviceData.accessToken
67 }
68 };
69 var session = auth.get('store').createRecord('session',
70 sessionData);
71 return session.save();
72 }).then( function(session){
73 auth.set('authToken', session.get('id'));
74
75 return session.get('user');
76 });
77 }
78 },
open an auth attempt
25 var LiveAuthService = Ember.Object.extend({
26 open: function(){
27 return this.signIn()
28 .then( this.normalizeServiceData );
29 },
30
31 signIn: function(){
32 var url = createAuthUrl();
33 return this.get('popup').
34 open(url, {width: 500, height: 510 });
35 },
36
37 normalizeServiceData: function(accessToken){
38 return {
39 name: 'live',
40 accessToken: accessToken
41 };
42 }
43 });
open windows live auth attempt
38 App.initializer({
39 name: 'Register Services',
40 initialize: function(container, app) {
41 registerServices(container);
42
43 // force creation of FacebookAuthService (to load the FB global)
44 container.lookup('auth:facebook');
45
46 app.inject('auth', 'popup', 'popups:authenticate');
47 }
48 });
Inject popup service
65 open: function(url, options) {
66 this.closeExistingWindow();
67 this.rejectExistingDeferred('canceled');
68 var deferred = this.generateNewDeferred();
69 var defaultedOptions = this.applyDefaultOptions(options);
70 this.popup = window.open(
71 url, 'authentication', parameterizeOptions(defaultedOptions)
72 );
73
74 if (this.popup) {
75 this.popup.focus();
76 $(window).on('message', this.boundMessageHandler);
77 } else {
78 this.rejectExistingDeferred('failed to open popup');
79 }
80
81 return deferred.promise;
82 },
30 createBoundMessageHandler: function(){
31 this.boundMessageHandler = function(event){
32 var matches, message = event.originalEvent.data;
33 if (!message || !(matches = message.match(/^setAccessToken:(.*)/))){
34 return;
35 }
36
37 if (matches[1]){
38 Ember.run(this, function(){
39 this.closeExistingWindow();
40 this.resolveExistingDeferred(matches[1]);
41 });
42 }
43 }.bind(this);
44 }.on('init'),
Listen for messages from the popup
Upon load, look for tokens
61 Ember.Application.initializer({
62 name: 'authentication',
63 initialize: function(container, app){
. . .
75 // Kind of feels like the in-popup logic should be elsewhere
76 var auth = container.lookup('session:adapter');
77 var token = auth.readAccessToken();
78 if (token && window.opener) {
79 // Don't go forward, we are just a popup with an accessToken
80 app.deferReadiness();
81 window.opener.postMessage(
82 'setAccessToken:'+token,
83 Overherd.settings.origin
84 );
85 }
. . .
94 }
95 });
Upon load, look for tokens
99 readAccessToken: function(){
100 var accessToken, match,
101 regex = /access_token=([^&]*)/,
102 hash = window.location.hash;
103
104 if (window.location.hash){
105 hash = window.location.hash;
106 if (match = hash.match(regex)) {
107 return match[1];
108 }
109 }
110 }
promises complete!
Other ideas
•localstorage for auth tokens
•how to recognize a cancelled sign in?
•we still check the token is valid
server-side
Try it
HereHere.co
Thanks!
@mixonic
httP://madhatted.com
matt.beale@madhatted.com

More Related Content

What's hot

What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020
What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020
What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020Matt Raible
 
Frontends w ithout javascript
Frontends w ithout javascriptFrontends w ithout javascript
Frontends w ithout javascriptStephen Lorello
 
Stateless Auth using OAuth2 & JWT
Stateless Auth using OAuth2 & JWTStateless Auth using OAuth2 & JWT
Stateless Auth using OAuth2 & JWTGaurav Roy
 
Bare-knuckle web development
Bare-knuckle web developmentBare-knuckle web development
Bare-knuckle web developmentJohannes Brodwall
 
OAuth 2.0 and Library
OAuth 2.0 and LibraryOAuth 2.0 and Library
OAuth 2.0 and LibraryKenji Otsuka
 
Using OAuth with PHP
Using OAuth with PHPUsing OAuth with PHP
Using OAuth with PHPDavid Ingram
 
Rest Security with JAX-RS
Rest Security with JAX-RSRest Security with JAX-RS
Rest Security with JAX-RSFrank Kim
 
The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...
The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...
The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...Codemotion
 
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4DEVCON
 
Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4DEVCON
 
Desenvolvimento web com Ruby on Rails (extras)
Desenvolvimento web com Ruby on Rails (extras)Desenvolvimento web com Ruby on Rails (extras)
Desenvolvimento web com Ruby on Rails (extras)Joao Lucas Santana
 
201410 2 fiware-orion-contextbroker
201410 2 fiware-orion-contextbroker201410 2 fiware-orion-contextbroker
201410 2 fiware-orion-contextbrokerFIWARE
 
FIWARE Developers Week_ Introduction to Managing Context Information at Large...
FIWARE Developers Week_ Introduction to Managing Context Information at Large...FIWARE Developers Week_ Introduction to Managing Context Information at Large...
FIWARE Developers Week_ Introduction to Managing Context Information at Large...FIWARE
 
Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013
Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013
Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013Aaron Parecki
 
Securing RESTful Payment APIs Using OAuth 2
Securing RESTful Payment APIs Using OAuth 2Securing RESTful Payment APIs Using OAuth 2
Securing RESTful Payment APIs Using OAuth 2Jonathan LeBlanc
 

What's hot (20)

OAuth 2.0
OAuth 2.0OAuth 2.0
OAuth 2.0
 
What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020
What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020
What the Heck is OAuth and OIDC - Denver Developer Identity Workshop 2020
 
Demystifying OAuth 2.0
Demystifying OAuth 2.0Demystifying OAuth 2.0
Demystifying OAuth 2.0
 
OAuth 2 Presentation
OAuth 2 PresentationOAuth 2 Presentation
OAuth 2 Presentation
 
Frontends w ithout javascript
Frontends w ithout javascriptFrontends w ithout javascript
Frontends w ithout javascript
 
Stateless Auth using OAuth2 & JWT
Stateless Auth using OAuth2 & JWTStateless Auth using OAuth2 & JWT
Stateless Auth using OAuth2 & JWT
 
Android ax app wcf
Android ax app wcfAndroid ax app wcf
Android ax app wcf
 
Android+ax+app+wcf
Android+ax+app+wcfAndroid+ax+app+wcf
Android+ax+app+wcf
 
Bare-knuckle web development
Bare-knuckle web developmentBare-knuckle web development
Bare-knuckle web development
 
OAuth 2.0 and Library
OAuth 2.0 and LibraryOAuth 2.0 and Library
OAuth 2.0 and Library
 
Using OAuth with PHP
Using OAuth with PHPUsing OAuth with PHP
Using OAuth with PHP
 
Rest Security with JAX-RS
Rest Security with JAX-RSRest Security with JAX-RS
Rest Security with JAX-RS
 
The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...
The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...
The Web Components interoperability challenge - Horacio Gonzalez - Codemotion...
 
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4
 
Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4
 
Desenvolvimento web com Ruby on Rails (extras)
Desenvolvimento web com Ruby on Rails (extras)Desenvolvimento web com Ruby on Rails (extras)
Desenvolvimento web com Ruby on Rails (extras)
 
201410 2 fiware-orion-contextbroker
201410 2 fiware-orion-contextbroker201410 2 fiware-orion-contextbroker
201410 2 fiware-orion-contextbroker
 
FIWARE Developers Week_ Introduction to Managing Context Information at Large...
FIWARE Developers Week_ Introduction to Managing Context Information at Large...FIWARE Developers Week_ Introduction to Managing Context Information at Large...
FIWARE Developers Week_ Introduction to Managing Context Information at Large...
 
Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013
Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013
Using ArcGIS with OAuth 2.0 - Esri DevSummit Dubai 2013
 
Securing RESTful Payment APIs Using OAuth 2
Securing RESTful Payment APIs Using OAuth 2Securing RESTful Payment APIs Using OAuth 2
Securing RESTful Payment APIs Using OAuth 2
 

Similar to Client-side Auth with Ember.js

Distributed Identities with OpenID
Distributed Identities with OpenIDDistributed Identities with OpenID
Distributed Identities with OpenIDBastian Hofmann
 
Distributed Identities with OpenID
Distributed Identities with OpenIDDistributed Identities with OpenID
Distributed Identities with OpenIDBastian Hofmann
 
How to create social apps for millions of users
How to create social apps for millions of users How to create social apps for millions of users
How to create social apps for millions of users Bastian Hofmann
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsLoiane Groner
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsBastian Hofmann
 
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...Maarten Balliauw
 
Crossing the Boundaries of Web Applications with OpenSocial
Crossing the Boundaries of Web Applications with OpenSocialCrossing the Boundaries of Web Applications with OpenSocial
Crossing the Boundaries of Web Applications with OpenSocialBastian Hofmann
 
Passwords suck, but centralized proprietary services are not the answer
Passwords suck, but centralized proprietary services are not the answerPasswords suck, but centralized proprietary services are not the answer
Passwords suck, but centralized proprietary services are not the answerFrancois Marier
 
OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...
OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...
OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...Maarten Balliauw
 
How to Build an Indivo X Personal Health App
How to Build an Indivo X Personal Health AppHow to Build an Indivo X Personal Health App
How to Build an Indivo X Personal Health AppBen Adida
 
Persona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwordsPersona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwordsFrancois Marier
 
使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務升煌 黃
 
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control
OAuth-as-a-serviceusing ASP.NET Web API and Windows Azure Access ControlOAuth-as-a-serviceusing ASP.NET Web API and Windows Azure Access Control
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access ControlMaarten Balliauw
 
OSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin BačovskýOSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin BačovskýNETWAYS
 
international PHP2011_Bastian Hofmann_Mashing up java script
international PHP2011_Bastian Hofmann_Mashing up java scriptinternational PHP2011_Bastian Hofmann_Mashing up java script
international PHP2011_Bastian Hofmann_Mashing up java scriptsmueller_sandsmedia
 
Pocket Authentication with OAuth on Firefox OS
Pocket Authentication with OAuth on Firefox OSPocket Authentication with OAuth on Firefox OS
Pocket Authentication with OAuth on Firefox OSChih-Hsuan Kuo
 

Similar to Client-side Auth with Ember.js (20)

Distributed Identities with OpenID
Distributed Identities with OpenIDDistributed Identities with OpenID
Distributed Identities with OpenID
 
Distributed Identities with OpenID
Distributed Identities with OpenIDDistributed Identities with OpenID
Distributed Identities with OpenID
 
Mashing up JavaScript
Mashing up JavaScriptMashing up JavaScript
Mashing up JavaScript
 
How to create social apps for millions of users
How to create social apps for millions of users How to create social apps for millions of users
How to create social apps for millions of users
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applications
 
Oauth
OauthOauth
Oauth
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web Apps
 
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control - W...
 
Crossing the Boundaries of Web Applications with OpenSocial
Crossing the Boundaries of Web Applications with OpenSocialCrossing the Boundaries of Web Applications with OpenSocial
Crossing the Boundaries of Web Applications with OpenSocial
 
Passwords suck, but centralized proprietary services are not the answer
Passwords suck, but centralized proprietary services are not the answerPasswords suck, but centralized proprietary services are not the answer
Passwords suck, but centralized proprietary services are not the answer
 
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access ControlOAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control
 
OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...
OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...
OAuth-as-a-service - using ASP.NET Web API and Windows Azure Access Control -...
 
How to Build an Indivo X Personal Health App
How to Build an Indivo X Personal Health AppHow to Build an Indivo X Personal Health App
How to Build an Indivo X Personal Health App
 
Persona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwordsPersona: in your browsers, killing your passwords
Persona: in your browsers, killing your passwords
 
使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務
 
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control
OAuth-as-a-serviceusing ASP.NET Web API and Windows Azure Access ControlOAuth-as-a-serviceusing ASP.NET Web API and Windows Azure Access Control
OAuth-as-a-service using ASP.NET Web API and Windows Azure Access Control
 
OSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin BačovskýOSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
 
Mashing up JavaScript
Mashing up JavaScriptMashing up JavaScript
Mashing up JavaScript
 
international PHP2011_Bastian Hofmann_Mashing up java script
international PHP2011_Bastian Hofmann_Mashing up java scriptinternational PHP2011_Bastian Hofmann_Mashing up java script
international PHP2011_Bastian Hofmann_Mashing up java script
 
Pocket Authentication with OAuth on Firefox OS
Pocket Authentication with OAuth on Firefox OSPocket Authentication with OAuth on Firefox OS
Pocket Authentication with OAuth on Firefox OS
 

More from Matthew Beale

Ember.js Module Loading
Ember.js Module LoadingEmber.js Module Loading
Ember.js Module LoadingMatthew Beale
 
LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017Matthew Beale
 
Interoperable Component Patterns
Interoperable Component PatternsInteroperable Component Patterns
Interoperable Component PatternsMatthew Beale
 
Ember Community 2016 - Be the Bark
Ember Community 2016 - Be the BarkEmber Community 2016 - Be the Bark
Ember Community 2016 - Be the BarkMatthew Beale
 
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)Matthew Beale
 
Aligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsAligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsMatthew Beale
 
New Component Patterns in Ember.js
New Component Patterns in Ember.jsNew Component Patterns in Ember.js
New Component Patterns in Ember.jsMatthew Beale
 
Scalable vector ember
Scalable vector emberScalable vector ember
Scalable vector emberMatthew Beale
 
Testing Ember Apps: Managing Dependency
Testing Ember Apps: Managing DependencyTesting Ember Apps: Managing Dependency
Testing Ember Apps: Managing DependencyMatthew Beale
 
Parse Apps with Ember.js
Parse Apps with Ember.jsParse Apps with Ember.js
Parse Apps with Ember.jsMatthew Beale
 
Snappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember AppsSnappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember AppsMatthew Beale
 
Containers & Dependency in Ember.js
Containers & Dependency in Ember.jsContainers & Dependency in Ember.js
Containers & Dependency in Ember.jsMatthew Beale
 
Complex Architectures in Ember
Complex Architectures in EmberComplex Architectures in Ember
Complex Architectures in EmberMatthew Beale
 
Ember and containers
Ember and containersEmber and containers
Ember and containersMatthew Beale
 

More from Matthew Beale (15)

Ember.js Module Loading
Ember.js Module LoadingEmber.js Module Loading
Ember.js Module Loading
 
LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017
 
Interoperable Component Patterns
Interoperable Component PatternsInteroperable Component Patterns
Interoperable Component Patterns
 
Ember Community 2016 - Be the Bark
Ember Community 2016 - Be the BarkEmber Community 2016 - Be the Bark
Ember Community 2016 - Be the Bark
 
Attribute actions
Attribute actionsAttribute actions
Attribute actions
 
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
 
Aligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsAligning Ember.js with Web Standards
Aligning Ember.js with Web Standards
 
New Component Patterns in Ember.js
New Component Patterns in Ember.jsNew Component Patterns in Ember.js
New Component Patterns in Ember.js
 
Scalable vector ember
Scalable vector emberScalable vector ember
Scalable vector ember
 
Testing Ember Apps: Managing Dependency
Testing Ember Apps: Managing DependencyTesting Ember Apps: Managing Dependency
Testing Ember Apps: Managing Dependency
 
Parse Apps with Ember.js
Parse Apps with Ember.jsParse Apps with Ember.js
Parse Apps with Ember.js
 
Snappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember AppsSnappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember Apps
 
Containers & Dependency in Ember.js
Containers & Dependency in Ember.jsContainers & Dependency in Ember.js
Containers & Dependency in Ember.js
 
Complex Architectures in Ember
Complex Architectures in EmberComplex Architectures in Ember
Complex Architectures in Ember
 
Ember and containers
Ember and containersEmber and containers
Ember and containers
 

Recently uploaded

Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityIES VE
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 

Recently uploaded (20)

Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a reality
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 

Client-side Auth with Ember.js

  • 3. 201 Created We build õ-age apps with Ember.js. We take teams from £ to • in no time flat.
  • 6. The Goal •Auth against multiple 3rd party services •Don’t be reloading the page •keep the complexity off the server
  • 7. One page, no reload Sign In Auth at Windows Live Signed In!
  • 8. OAuth2 •access to resources via tokens •Several token types •a different flow for each type
  • 9. +----------+ | Resource | | Owner | | | +----------+ v | Resource Owner (A) Password Credentials | v +---------+ +---------------+ | |>--(B)---- Resource Owner ------->| | | | Password Credentials | Authorization | | Client | | Server | | |<--(C)---- Access Token ---------<| | | | (w/ Optional Refresh Token) | | +---------+ +---------------+ resource owner password credentials grant Do not use this
  • 10. +----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI ---->| | | User- | | Authorization | | Agent -+----(B)-- User authenticates --->| Server | | | | | | -+----(C)-- Authorization Code ---<| | +-|----|---+ +---------------+ | | ^ v (A) (C) | | | | | | ^ v | | +---------+ | | | |>---(D)-- Authorization Code ---------' | | Client | & Redirection URI | | | | | |<---(E)----- Access Token -------------------' +---------+ (w/ Optional Refresh Token) Authorization code grant
  • 11. +----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI --->| | | User- | | Authorization | | Agent -|----(B)-- User authenticates -->| Server | | | | | | |<---(C)--- Redirection URI ----<| | | | with Access Token +---------------+ | | in Fragment | | +---------------+ | |----(D)--- Redirection URI ---->| Web-Hosted | | | without Fragment | Client | | | | Resource | | (F) |<---(E)------- Script ---------<| | | | +---------------+ +-|--------+ | | (A) (G) Access Token | | ^ v +---------+ | | | Client | | | +---------+ implicit grant
  • 12. +----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI --->| | | User- | | Authorization | | Agent -|----(B)-- User authenticates -->| Server | | | | | | |<---(C)--- Redirection URI ----<| | | | with Access Token +---------------+ | | in Fragment | | +---------------+ | |----(D)--- Redirection URI ---->| Web-Hosted | | | without Fragment | Client | | | | Resource | | (F) |<---(E)------- Script ---------<| | | | +---------------+ +-|--------+ | | (A) (G) Access Token | | ^ v +---------+ | | | Client | | | +---------+ implicit grant browser Facebook Auth website server website User Facebook connect
  • 13. Implicit Grant supported by •facebook (behind sdk) •google •soundcloud •box.net •windows live ! •& more
  • 15. Abstractions •session manager (controller) •Oauth adapter •live, FB specific adapters •popup manager
  • 16. 6 <div {{bind-attr class=":sign-in-menu isOpen:open:closed"}}> 7 <a class="sign-in-button facebook" href="#" {{action "selectService" "facebook"}}> 8 Sign In with Facebook 9 </a> 10 <a class="sign-in-button windows" href="#" {{action "selectService" "live"}}> 11 Sign In with Windows Live 12 </a> 13 <div class="mobile-show menu-footer subheader"> 14 {{link-to 'Leaderboard' 'leaderboard'}} 15 {{link-to 'About' 'map.about'}} 16 </div> 17 </div>
  • 17. 36 signIn: function(service){ 37 var session = this.get('session'), 38 route = this; 39 session.open(service) 40 .then(function(){ 41 if (route.router.isActive('map')) { 42 route.disconnectOutlet({ 43 outlet: 'prompt', 44 parentView: 'map/neighborhood' 45 }); 46 } 47 var lastTransition = session.get('afterRedirect'); 48 if (lastTransition) { 49 lastTransition.retry(); 50 } else if (route.router.isActive('sign_in')) { 51 route.transitionTo(''); 52 } 53 }).catch(Ember.Logger.error); 54 },
  • 18. 61 Ember.Application.initializer({ 62 name: 'authentication', 63 initialize: function(container, app){ . . . 65 import Session from 'appkit/controllers/session'; 66 container.register('session:main', Session); 67 app.inject('controller', 'session', 'session:main'); 68 app.inject('route', 'session', 'session:main'); . . . 95 }); inject session
  • 19. 9 open: function(credentials){ 10 var session = this; 11 session.set('isAuthenticating', true); 12 13 return this.get('adapter').open(credentials, this) 14 .then(function(user){ 15 session.setProperties({ 16 isAuthenticated: true, 17 currentUser: user 18 }); 19 return user; 20 }).catch(function(err){ 21 if (err === 'canceled') { 22 return; // no-op 23 } else { 24 return Ember.RSVP.reject(err); 25 } 26 }).finally(function(){ 27 session.set('isAuthenticating', false); 28 }); 29 }, opening a session
  • 20. 61 Ember.Application.initializer({ 62 name: 'authentication', 63 initialize: function(container, app){ . . . 70 import Auth from 'appkit/models/auth'; 71 container.register('session:adapter', Auth); 72 app.inject('session:adapter', 'store', 'store:main'); 73 app.inject('session:main', 'adapter', 'session:adapter'); . . . 95 }); Inject Auth adapter
  • 21. 50 // returns a promise that resolves to a user 51 open: function(serviceName){ 52 var auth = this; 53 var authService = this.container.lookup('auth:' + serviceName); 54 55 if (!authService) { 56 return Ember.RSVP.reject('unrecognized service auth:' + 57 serviceName); 58 } else { 59 return authService.open() 60 .then( function(serviceData) { 61 // create a session 62 var sessionData = { 63 authData: { 64 name: serviceData.name, 65 id: serviceData.id, 66 accessToken: serviceData.accessToken 67 } 68 }; 69 var session = auth.get('store').createRecord('session', 70 sessionData); 71 return session.save(); 72 }).then( function(session){ 73 auth.set('authToken', session.get('id')); 74 75 return session.get('user'); 76 }); 77 } 78 }, open an auth attempt
  • 22. 25 var LiveAuthService = Ember.Object.extend({ 26 open: function(){ 27 return this.signIn() 28 .then( this.normalizeServiceData ); 29 }, 30 31 signIn: function(){ 32 var url = createAuthUrl(); 33 return this.get('popup'). 34 open(url, {width: 500, height: 510 }); 35 }, 36 37 normalizeServiceData: function(accessToken){ 38 return { 39 name: 'live', 40 accessToken: accessToken 41 }; 42 } 43 }); open windows live auth attempt
  • 23. 38 App.initializer({ 39 name: 'Register Services', 40 initialize: function(container, app) { 41 registerServices(container); 42 43 // force creation of FacebookAuthService (to load the FB global) 44 container.lookup('auth:facebook'); 45 46 app.inject('auth', 'popup', 'popups:authenticate'); 47 } 48 }); Inject popup service
  • 24. 65 open: function(url, options) { 66 this.closeExistingWindow(); 67 this.rejectExistingDeferred('canceled'); 68 var deferred = this.generateNewDeferred(); 69 var defaultedOptions = this.applyDefaultOptions(options); 70 this.popup = window.open( 71 url, 'authentication', parameterizeOptions(defaultedOptions) 72 ); 73 74 if (this.popup) { 75 this.popup.focus(); 76 $(window).on('message', this.boundMessageHandler); 77 } else { 78 this.rejectExistingDeferred('failed to open popup'); 79 } 80 81 return deferred.promise; 82 },
  • 25. 30 createBoundMessageHandler: function(){ 31 this.boundMessageHandler = function(event){ 32 var matches, message = event.originalEvent.data; 33 if (!message || !(matches = message.match(/^setAccessToken:(.*)/))){ 34 return; 35 } 36 37 if (matches[1]){ 38 Ember.run(this, function(){ 39 this.closeExistingWindow(); 40 this.resolveExistingDeferred(matches[1]); 41 }); 42 } 43 }.bind(this); 44 }.on('init'), Listen for messages from the popup
  • 26. Upon load, look for tokens 61 Ember.Application.initializer({ 62 name: 'authentication', 63 initialize: function(container, app){ . . . 75 // Kind of feels like the in-popup logic should be elsewhere 76 var auth = container.lookup('session:adapter'); 77 var token = auth.readAccessToken(); 78 if (token && window.opener) { 79 // Don't go forward, we are just a popup with an accessToken 80 app.deferReadiness(); 81 window.opener.postMessage( 82 'setAccessToken:'+token, 83 Overherd.settings.origin 84 ); 85 } . . . 94 } 95 });
  • 27. Upon load, look for tokens 99 readAccessToken: function(){ 100 var accessToken, match, 101 regex = /access_token=([^&]*)/, 102 hash = window.location.hash; 103 104 if (window.location.hash){ 105 hash = window.location.hash; 106 if (match = hash.match(regex)) { 107 return match[1]; 108 } 109 } 110 }
  • 29. Other ideas •localstorage for auth tokens •how to recognize a cancelled sign in? •we still check the token is valid server-side