UNPKG

5.26 kBMarkdownView Raw
1# Artsy Passport
2
3[![CircleCI](https://circleci.com/gh/artsy/artsy-passport.svg?style=svg)](https://circleci.com/gh/artsy/artsy-passport)
4
5Wires up the common auth handlers, and related security concerns, for Artsy's [Ezel](https://github.com/artsy/ezel)-based apps using [passport](http://passportjs.org/). Used internally at Artsy to DRY up authentication code.
6
7## Breaking changes
8
9We mave migrated this app from the module "artsy-passport" to "@artsy/passport", and called that v1.
10
11## Setup
12
13#### Make sure you first mount session, body parser, and start [@artsy/xapp](https://github.com/artsy/artsy-xapp).
14
15```coffee
16app.use express.bodyParser()
17app.use express.cookieParser('foobar')
18app.use express.cookieSession()
19artsyXapp.init -> app.listen()
20```
21
22#### Then mount Artsy Passport passing a big configuration hash.
23
24_Values indicate defaults._
25
26```coffee
27app.use artsyPassport
28
29 CurrentUser: # The CurrentUser Backbone model
30
31 # Pass in env vars
32 # ----------------
33 FACEBOOK_ID: # Facebook app ID
34 FACEBOOK_SECRET: # Facebook app secret
35 ARTSY_ID: # Artsy client id
36 ARTSY_SECRET: # Artsy client secret
37 ARTSY_URL: # SSL Artsy url e.g. https://artsy.net
38 APP_URL: # Url pointing back to your app e.g. http://flare.artsy.net
39 SEGMENT_WRITE_KEY: # Segment write key to track signup
40
41 # Defaults you probably don't need to touch
42 # -----------------------------------------
43
44 # Social auth
45 facebookPath: '/users/auth/facebook'
46 facebookCallbackPath: '/users/auth/facebook/callback'
47
48 # Landing pages
49 loginPagePath: '/log_in'
50 signupPagePath: '/sign_up'
51 settingsPagePath: '/user/edit'
52 afterSignupPagePath: '/personalize'
53
54 # Misc
55 logoutPath: '/users/sign_out'
56 userKeys: [
57 'id', 'type', 'name', 'email', 'phone', 'lab_features',
58 'default_profile_id', 'has_partner_access', 'collector_level'
59 ]
60```
61
62The keys are cased so it's convenient to pass in a configuration hash. A minimal setup could look like this:
63
64```coffee
65app.use artsyPassport _.extend config,
66 CurrentUser: CurrentUser
67```
68
69**Note:** CurrentUser must be a Backbone model with typical `get` and `toJSON` methods.
70
71#### Create a login form pointing to your paths.
72
73```jade
74h1 Login
75pre!= error
76a( href=ap.facebookPath ) Login via Facebook
77form( action=ap.loginPagePath, method='POST' )
78 h3 Login via Email
79 input( name='name' )
80 input( name='email' )
81 input( name='password' )
82 input( type="hidden" name="_csrf" value=csrfToken )
83 button( type='submit' ) Login
84```
85
86#### And maybe a signup form...
87
88```jade
89h1 Signup
90pre!= error
91a( href=ap.facebookPath ) Signup via Facebook
92form( action=ap.signupPagePath, method='POST' )
93 h3 Signup via Email
94 input( name='name' )
95 input( name='email' )
96 input( name='password' )
97 input( type="hidden" name="_csrf" value=csrfToken )
98 button( type='submit' ) Signup
99```
100
101#### And maybe a settings page for linking accounts...
102
103```jade
104h2 Linked Accounts
105pre!= error
106- providers = user.get('authentications').map(function(a) { return a.provider })
107if providers.indexOf('facebook') > -1
108 | Connected Facebook
109else
110 a( href=ap.facebookPath ) Connect Facebook
111```
112
113#### Render the pages
114
115```coffee
116{ loginPagePath, signupPagePath, settingsPagePath, afterSignupPagePath } = artsyPassport.options
117
118app.get loginPagePath, (req, res) -> res.render 'login'
119app.get signupPagePath, (req, res) -> res.render 'signup'
120app.get settingsPagePath, (req, res) -> res.render 'settings'
121app.get afterSignupPagePath, (req, res) -> res.render 'personalize'
122```
123
124#### Access a logged in Artsy user in a variety of ways...
125
126In your server-side templates
127
128```jade
129h1 Hello #{user.get('name')}
130```
131
132In your client-side code
133
134```coffee
135CurrentUser = require '../models/current_user.coffee'
136sd = require('sharify').data
137
138user = new CurrentUser(sd.CURRENT_USER)
139```
140
141In your routers
142
143```coffee
144app.get '/', (req, res) ->
145 res.send 'Hello ' + req.user.get('name')
146```
147
148_These forms of user will be null if they're not logged in._
149
150## Sanitize Redirect
151
152If you implement a fancier auth flow that involves client-side redirecting back, you may find this helper useful in avoiding ["open redirect"](https://github.com/artsy/artsy-passport/issues/68) attacks.
153
154```coffee
155sanitizeRedirect = require 'artsy-passport/sanitize-redirect'
156
157location.href = sanitizeRedirect "http://artsy.net%0D%0Aattacker.com/"
158# Notices the url isn't pointing at artsy.net, so just redirects to /
159```
160
161## Contributing
162
163Add a `local.artsy.net` entry into your /etc/hosts
164
165```
166127.0.0.1 localhost
167#...
168127.0.0.1 local.artsy.net
169```
170
171Install node modules `npm install` then write a ./config.coffee that looks something like this:
172
173```coffee
174module.exports =
175 FACEBOOK_ID: ''
176 FACEBOOK_SECRET: ''
177 ARTSY_ID: ''
178 ARTSY_SECRET: ''
179 ARTSY_URL: 'https://api.artsy.net'
180 APP_URL: 'http://local.artsy.net:4000'
181 # An Artsy user that's linked to Facebook
182 ARTSY_EMAIL: 'craig@artsy.net'
183 ARTSY_PASSWORD: ''
184 FACEBOOK_EMAIL: 'craigspaeth@gmail.com'
185 FACEBOOK_PASSWORD: ''
186```
187
188Then you can check the example by running `npm run example` and opening [localhost:4000](http://localhost:4000).
189
190The tests are a combination of integration and middleware unit tests. To run the whole suite use `npm test`.
191
192## Publishing to npm
193
194```
195yarn compile
196yarn publish
197```