1 | ember-cli-gatekeeper
|
2 | ====================
|
3 |
|
4 | EmberJS add-on for [blueprint-gatekeeper](https://github.com/onehilltech/blueprint-gatekeeper)
|
5 |
|
6 | [![npm version](https://img.shields.io/npm/v/ember-cli-gatekeeper.svg?maxAge=2592000)](https://www.npmjs.com/package/ember-cli-gatekeeper)
|
7 | [![Dependencies](https://david-dm.org/onehilltech/ember-cli-gatekeeper.svg)](https://david-dm.org/onehilltech/ember-cli-gatekeeper)
|
8 |
|
9 |
|
10 | Compatibility
|
11 | ------------------------------------------------------------------------------
|
12 |
|
13 | * Ember.js v3.4 or above
|
14 | * Ember CLI v2.13 or above
|
15 | * Node.js v8 or above
|
16 |
|
17 |
|
18 | Installation
|
19 | --------------
|
20 |
|
21 | npm install ember-cli-blueprint-helpers --save-dev # temp workaround
|
22 | ember install ember-cli-gatekeeper
|
23 |
|
24 | Getting Started
|
25 | ----------------
|
26 |
|
27 | ## Defining the configuration
|
28 |
|
29 | Update the `ENV` variable in `config/environment.js` with the required
|
30 | configuration values:
|
31 |
|
32 | | Name | Description | Required | Default Value |
|
33 | |-------|-------------|----------|---------------|
|
34 | | gatekeeper.baseUrl | Location of blueprint-gatekeeper | Yes | |
|
35 | | gatekeeper.startRoute | Default route name, or url, to transition to after login | | index |
|
36 | | gatekeeper.signInRoute | Name of the sign in route | | sign-in |
|
37 | | gatekeeper.tokenOptions.client_id | Client id | Yes | |
|
38 | | gatekeeper.tokenOptions.client_secret | Client secret | | |
|
39 |
|
40 | The client secret should only be used if the web application is installed in
|
41 | a trusted environment, such as a mobile application via [ember-cordova](http://embercordova.com/).
|
42 |
|
43 | Here is an example `config/environment.js` with the Gatekeeper configuration:
|
44 |
|
45 | ```javascript 1.6
|
46 | let ENV = {
|
47 | // ...
|
48 |
|
49 | EmberENV: {
|
50 | FEATURES: {
|
51 | // This must be enabled for account adapter to work.
|
52 | 'ds-improved-ajax': true
|
53 | }
|
54 |
|
55 | // ...
|
56 | },
|
57 |
|
58 | gatekeeper: {
|
59 | baseUrl: 'https://api.onehilltech.com/gatekeeper',
|
60 |
|
61 | tokenOptions: {
|
62 | client_id: '59ee923e1fd71c2ae68ade62',
|
63 | client_secret: '1234567890'
|
64 | }
|
65 | }
|
66 | }
|
67 | ```
|
68 |
|
69 | ## Protecting application routes
|
70 |
|
71 | Protected application routes are routes that require the user to be signed in
|
72 | to access. Creating protected application route is very simple.
|
73 |
|
74 | First, create the route using [ember-cli](https://ember-cli.com/).
|
75 |
|
76 | ember g route [name]
|
77 |
|
78 | Then, import the `Authenticated` mixin from `ember-cli-gatekeeper` and apply it
|
79 | to the route.
|
80 |
|
81 | ```javascript
|
82 | // app/routes/comments.js
|
83 |
|
84 | import Route from '@ember/routing/route';
|
85 | import Authenticated from 'ember-cli-gatekeeper/mixins/authenticated';
|
86 |
|
87 | export default Route.extend (Authenticated, {
|
88 | model () {
|
89 | // Get the user for the current session.
|
90 | let currentUser = this.get ('currentUser');
|
91 | return this.get ('store').query ('comments', {user: user.id});
|
92 | }
|
93 | });
|
94 | ```
|
95 |
|
96 | The [gatekeeper](https://github.com/onehilltech/ember-cli-gatekeeper/blob/master/addon/services/gatekeeper.js)
|
97 | service is injected into all routes. The
|
98 | [Authenticated](https://github.com/onehilltech/ember-cli-gatekeeper/blob/master/addon/mixins/authenticated.js)
|
99 | class provides the `currentUser` property, which gives you access to the
|
100 | [account model](https://github.com/onehilltech/ember-cli-gatekeeper/blob/master/addon/models/account.js)
|
101 | (less the password) for the signed in user.
|
102 |
|
103 | > When this route is accessed and the user is not signed in, the user will
|
104 | > be transitioned to the `sign-in` route (see [Configuration](#defining-the-configuration)). After
|
105 | > the user signs in, the user will be transitioned back to the original route or the `startRoute`
|
106 | > defined in the configuration.
|
107 |
|
108 | ## Accessing protected data
|
109 |
|
110 | [ember-data](https://github.com/emberjs/data) uses data models to access resources on
|
111 | a remote server. When using Gatekeeper, the routes for accessing these resources are
|
112 | protected via an authorization token. To get this authorization token into each
|
113 | [ember-data](https://github.com/emberjs/data) request, the adapters in your application
|
114 | (either the application or model-specific adapters) must extend the `RESTAdapter` in
|
115 | Gatekeeper.
|
116 |
|
117 | ```javascript
|
118 | // app/adapters/application.js
|
119 |
|
120 | import RESTAdapter from 'ember-cli-gatekeeper/-lib/user/adapters/rest';
|
121 |
|
122 | export default RESTAdapter.extend({
|
123 |
|
124 | });
|
125 | ```
|
126 |
|
127 | You can then continue [configuring the adapter](https://emberjs.com/api/ember-data/3.3/classes/DS.RESTAdapter)
|
128 | as normal.
|
129 |
|
130 | ## Signing in a user
|
131 |
|
132 | To sign in a user, you need a route with a form that collects the user's username
|
133 | and password. The Gatekeeper add-on provides a form that can be used to sign-in
|
134 | a user.
|
135 |
|
136 | ```handlebars
|
137 | {{gatekeeper-sign-in complete=(action "complete")}}
|
138 | ```
|
139 |
|
140 | This form needs to be added to your sign-in route. When the user has signed in
|
141 | successfully, the `complete` action is invoked. At this point, you are free to
|
142 | transition to any route in the application.
|
143 |
|
144 | A standard practice is to route to either the start route, or to the previous
|
145 | route the user tried to access when they were not signed in. If you want this
|
146 | behavior, then apply the `Completed` mixin to the controller for the sign in
|
147 | route.
|
148 |
|
149 | ```javascript
|
150 | import Controller from '@ember/controller';
|
151 | import Completed from 'ember-cli-gatekeeper/mixins/completed';
|
152 |
|
153 | export default Controller.extend (Completed, {
|
154 |
|
155 | });
|
156 |
|
157 | ```
|
158 |
|
159 | Now, the user will either be routed to the start route, or the previously accessed
|
160 | route before being routed to the sign in route, when they successfully sign in.
|
161 |
|
162 | ### Using reCAPTCHA
|
163 |
|
164 | Gatekeeper uses different public/private key verification schemes to ensure that robots are
|
165 | not accessing the system. When developing a web application, it is not safe
|
166 | to place a secret in an EmberJS application because it will be accessible to site visitors.
|
167 | We therefore recommend you use a reCAPTCHA service, such as Google reCAPTCHA, to verify users
|
168 | are not robots.
|
169 |
|
170 | Gatekeeper provides out-of-the-box support for Google reCAPTCHA via the
|
171 | [ember-cli-google-recaptcha](https://github.com/onehilltech/ember-cli-google-recaptcha) add-on.
|
172 | First, you have to do is add your `siteKey` to `config/environment.js`:
|
173 |
|
174 | ```javascript
|
175 | let ENV = {
|
176 | // ...
|
177 |
|
178 | 'ember-cli-google': {
|
179 | recaptcha: {
|
180 | siteKey: 'This is where my siteKey goes'
|
181 | }
|
182 | }
|
183 | };
|
184 | ```
|
185 |
|
186 | Next, you replace the standard sign in component with the reCAPTCHA sign in component.
|
187 |
|
188 | ```handlebars
|
189 | {{gatekeeper-sign-in-with-recaptcha recaptcha=v2 complete=(action "complete")}}
|
190 | ```
|
191 |
|
192 | > Set `recaptcha="invisible"` to use invisible reCAPTCHA.
|
193 |
|
194 | ## Signing out a user
|
195 |
|
196 | A signed in user can be signed out from any where in the application as long as you
|
197 | have access to the `session` service.
|
198 |
|
199 | > The `session` service is injected into all routes and controllers.
|
200 |
|
201 | ```javascript
|
202 | // app/controllers/index.js
|
203 |
|
204 | import Controller from '@ember/controller';
|
205 |
|
206 | export default Controller.extend({
|
207 | actions: {
|
208 | signOut () {
|
209 | this.get ('session').signOut ().then (() => {
|
210 | this.replaceRoute ('sign-in');
|
211 | });
|
212 | }
|
213 | }
|
214 | });
|
215 | ```
|
216 |
|
217 | ## Allowing users to create accounts
|
218 |
|
219 | The Gatekeeper add-on also provides a default form for creating an new account. You use
|
220 | it in a similar manner as signing in a user. First, add the sign up form to the route for
|
221 | signing up a user, and configure the form to your needs.
|
222 |
|
223 | ```handlebars
|
224 | {{gatekeeper-sign-up complete=(action "complete")}}
|
225 | ```
|
226 |
|
227 | > The Gatekeeper add-on also has sign up components that supports reCAPTCHA.
|
228 |
|
229 | Then, apply the `Completed` mixin to the controller for the sign up route.
|
230 |
|
231 | ```javascript
|
232 | import Controller from '@ember/controller';
|
233 | import Completed from 'ember-cli-gatekeeper/mixins/completed';
|
234 |
|
235 | export default Controller.extend (Completed, {
|
236 |
|
237 | });
|
238 | ```
|
239 |
|
240 | > The client registered with the server must have the `gatekeeper.account.create` scope.
|
241 | > Otherwise, the client will not be authorized to create the account.
|
242 |
|
243 | ### Manually creating an account
|
244 |
|
245 | We use the `account` model to create user accounts. We assume that you have
|
246 | created a template to gather the `username`, `password`, and `email`
|
247 | from the user and have a controller action to that creates the account:
|
248 |
|
249 | ```javascript
|
250 | import Controller from '@ember/controller';
|
251 |
|
252 | export default Controller.extend({
|
253 | actions: {
|
254 | createAccount () {
|
255 | let {email, username, password} = this.getProperties (['email', 'username', 'password']);
|
256 | let account = this.get ('store').createRecord ('account', {username, password, email});
|
257 | let adapterOptions = {signIn: true};
|
258 |
|
259 | account.save ({adapterOptions}).then (account => {
|
260 | // You can transition to a protected application route
|
261 | }).catch (reason => {
|
262 | // Display error message to user
|
263 | });
|
264 | }
|
265 | }
|
266 | });
|
267 | ```
|
268 |
|
269 | The `save()` method takes an optional `adapterOptions` property that allows you to
|
270 | sign in the user when the account is created. The advantage of doing this it that
|
271 | it allows you to transition to a protected application route after account creation,
|
272 | or access [protected data](#accessing-protected-data) as part of the creation process.
|
273 | Otherwise, the user will have to sign in after creating the account to access a
|
274 | protected application route.
|
275 |
|
276 | Happy Coding!
|