no_password_auth 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README-ES.md +230 -0
  4. data/README.md +230 -0
  5. data/Rakefile +18 -0
  6. data/app/assets/config/no_password/manifest.js +4 -0
  7. data/app/assets/config/no_password/tailwind.config.js +61 -0
  8. data/app/assets/images/no_password/aoo.svg +1 -0
  9. data/app/assets/javascripts/no_password/application.js +4 -0
  10. data/app/assets/javascripts/no_password/controllers/alert_controller.js +31 -0
  11. data/app/assets/javascripts/no_password/controllers/application.js +10 -0
  12. data/app/assets/javascripts/no_password/controllers/index.js +11 -0
  13. data/app/assets/stylesheets/no_password/application.css +15 -0
  14. data/app/assets/stylesheets/no_password/application.tailwind.css +34 -0
  15. data/app/controllers/concerns/no_password/controller_helpers.rb +45 -0
  16. data/app/controllers/concerns/no_password/web_tokens.rb +33 -0
  17. data/app/controllers/no_password/application_controller.rb +4 -0
  18. data/app/controllers/no_password/session_confirmations_controller.rb +46 -0
  19. data/app/controllers/no_password/sessions_controller.rb +49 -0
  20. data/app/helpers/no_password/application_helper.rb +4 -0
  21. data/app/helpers/no_password/no_password_helper.rb +13 -0
  22. data/app/jobs/no_password/application_job.rb +4 -0
  23. data/app/mailers/no_password/application_mailer.rb +6 -0
  24. data/app/mailers/no_password/sessions_mailer.rb +16 -0
  25. data/app/models/no_password/application_record.rb +5 -0
  26. data/app/models/no_password/session.rb +20 -0
  27. data/app/use_cases/no_password/session_manager.rb +51 -0
  28. data/app/views/layouts/no_password/application.html.erb +26 -0
  29. data/app/views/layouts/no_password/mailer.html.erb +106 -0
  30. data/app/views/layouts/no_password/mailer.text.erb +6 -0
  31. data/app/views/no_password/application/_notification.html.erb +55 -0
  32. data/app/views/no_password/session_confirmations/_form.html.erb +19 -0
  33. data/app/views/no_password/session_confirmations/edit.html.erb +15 -0
  34. data/app/views/no_password/session_confirmations/update.turbo_stream.erb +3 -0
  35. data/app/views/no_password/sessions/_form.html.erb +16 -0
  36. data/app/views/no_password/sessions/create.turbo_stream.erb +3 -0
  37. data/app/views/no_password/sessions/new.html.erb +15 -0
  38. data/app/views/no_password/sessions_mailer/send_token.html.erb +14 -0
  39. data/app/views/no_password/sessions_mailer/send_token.text.erb +12 -0
  40. data/config/initializers/importmap.rb +17 -0
  41. data/config/locales/en/flash.en.yml +11 -0
  42. data/config/locales/en/forms.en.yml +9 -0
  43. data/config/locales/en/mailers.en.yml +21 -0
  44. data/config/locales/en/views.en.yml +12 -0
  45. data/config/locales/es/flash.es.yml +11 -0
  46. data/config/locales/es/forms.es.yml +9 -0
  47. data/config/locales/es/mailers.es.yml +18 -0
  48. data/config/locales/es/views.es.yml +13 -0
  49. data/config/locales/models.en.yml +8 -0
  50. data/config/locales/models.es.yml +8 -0
  51. data/config/locales/models.yml +8 -0
  52. data/config/routes.rb +7 -0
  53. data/db/migrate/20211202211706_create_no_password_sessions.rb +16 -0
  54. data/docs/aoorora-demo.gif +0 -0
  55. data/docs/dummy-app.png +0 -0
  56. data/lib/generators/no_password/install_generator.rb +30 -0
  57. data/lib/generators/no_password/install_templates_generator.rb +27 -0
  58. data/lib/generators/no_password/tailwind_config_generator.rb +9 -0
  59. data/lib/generators/no_password/templates/app/assets/config/no_password/tailwind.config.js.tt +57 -0
  60. data/lib/generators/no_password/templates/config/initializers/no_password.rb +10 -0
  61. data/lib/no_password/engine.rb +11 -0
  62. data/lib/no_password/railtie.rb +7 -0
  63. data/lib/no_password/version.rb +3 -0
  64. data/lib/no_password.rb +42 -0
  65. data/lib/tasks/install.rake +17 -0
  66. data/lib/tasks/no_password_tasks.rake +4 -0
  67. data/lib/tasks/tailwind.rake +23 -0
  68. metadata +227 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 21b0742b67d08d1694998ce4a0cb4c5460eaa07d28b4481bd2f081580dfe2cad
4
+ data.tar.gz: 2148a8faa5cccc2d52f33708d862f116d69b832a1043dfc18aa9900d1c9a0ae3
5
+ SHA512:
6
+ metadata.gz: 395f222367f5bfe903e1947e2fb4246ecad6c11c904679dc78ed3dc43e0a751066aa6cce9f016d2fd6e6aca396321898d240775ba1348a43aa1716c11fcdb1a3
7
+ data.tar.gz: d8beccec2e849f9a407ddfd27850e4a2c1568f4189564424578e669d5030c39f1b25653f324c157c93a4cab5daffa4908a59e5042ae04ac3213bdba11a5bcca0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2021 armando escalier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README-ES.md ADDED
@@ -0,0 +1,230 @@
1
+ # NoPassword
2
+
3
+ NoPassword es una gema de Ruby on Rails que realiza autenticación de sesiones a través de token o link mágico enviado a correo electrónico de usuario, no se necesitan contraseñas.
4
+
5
+ ## Tabla de contenido
6
+
7
+ - [Requerimientos](#requerimientos)
8
+ - [Instalación](#instalación)
9
+ - [Configuración](#configuración)
10
+ - [Inicializador](#configurar-inicializador)
11
+ - [Configurar idiomas](#configurar-idiomas)
12
+ - [Configuración para emails](#configuración-para-emails)
13
+ - [Personalización de vistas](#personalización-de-vistas)
14
+ - [Uso de NoPassword](#uso-de-nopassword)
15
+ - [Filtros de controlador](#filtros-de-controlador)
16
+ - [Helper methods](#helper-methods)
17
+ - [Callback](#callback)
18
+ - [Generadores](#generadores)
19
+ - [Desarrollo y pruebas de la gema](#desarrollo-y-pruebas-de-la-gema)
20
+ - [Licencia](#licencia)
21
+
22
+ ## Requerimientos
23
+
24
+ NoPassword es un Rails Engine que requiere de Ruby on Rails 7.0 o mejor, así como Ruby 3.0.2 o mejor para funcionar.
25
+ NoPassword tiene dos vistas, una para solicitar un token o código y otra para ingresar el token. Las dos vistas hacen uso de TailwindCSS via la gema [tailwindcss-rails](https://github.com/rails/tailwindcss-rails) y de StimulusJS a través de Importmaps.
26
+
27
+ En caso de requieras personalizar la forma en como se sirven los assets, por ejemplo, con Webpacker o los Bundlings de Rails, entonces puedes realizar la [Personalización de vistas](#personalización-de-vistas) para ajustarlas a tus necesidades.
28
+
29
+ ## Instalación
30
+
31
+ Para instalar NoPassword solamente hay que añadir la gema a tu `Gemfile`.
32
+ ```ruby
33
+ gem "no_password_auth", require: "no_password"
34
+ ```
35
+ También puedes instalar la gema directamente desde el repositorio de github con:
36
+ ```ruby
37
+ gem "no_password_auth", git: "https://github.com/creditario/nopassword.git", require: "no_password"
38
+ ```
39
+
40
+ En ambos casos hay que ejecutar `bundle` para instalar la dependencia.
41
+
42
+ El siguente paso consiste en instalar las migraciones y el archivo inicializador de NoPassword.
43
+
44
+ ```bash
45
+ $ bin/rails no_password:install
46
+ ```
47
+ Recuerda ejecutar las migraciones antes de continuar.
48
+ ```bash
49
+ $ bin/rails db:migrate
50
+ ```
51
+
52
+ En la siguiente sección se explicará las opciones de configuración de NoPassword mediante su inicializador.
53
+
54
+ ## Configuración
55
+ Al instalar NoPassword automáticamente el Engine se monta en `/p` dentro del archivo `config/routes.rb`.
56
+
57
+ ### Configurar inicializador
58
+ NoPassword provee un inicializador en `config/initializers/no_password.rb`. Aquí es posible cambiar el comportamiento de la expiración de la sesión después de haber iniciado, por omisión expira después de dos horas `session_expiration`. Otra opción disponible es indicar en cuanto tiempo expira un token que no ha sido reclamado, por omisión expira en 15 minutos `token_expiration`.
59
+
60
+ `secret_key` permite configurar la llave que firma las URLs de los links mágicos para iniciar sesión. Si el valor es nulo, entonces hace uso del `secret_key` de Ruby on Rails.
61
+
62
+ ```ruby
63
+ NoPassword.configure do |config|
64
+ # Session expiration time
65
+ # config.session_expiration = 2.hours
66
+ #
67
+ # Token expiration time
68
+ # config.token_expiration = 15.minutes
69
+ #
70
+ # Secret key to cypher tokens, if none, then Rails secret key is used
71
+ # config.secret_key = nil
72
+ end
73
+ ```
74
+
75
+ ### Configurar idiomas
76
+ NoPassword cuenta con archivos de traducción para español (`:es`) e inglés (`:en`). El idioma en que se muestre dependederá de la configuración en tu `config/application.rb`. Ejemplo.
77
+
78
+ ```ruby
79
+ config.i18n.default_locale = :es
80
+ config.i18n.available_locales = [:en, :es]
81
+ ```
82
+
83
+ ### Configuración para emails
84
+ ActionMailer es utilizado por la gema para enviar el correo electrónico con el token y el link mágico. Los correos se envian con la ayuda de ActiveJob cuando existe alguna estrategía configurada para este como caso, por ejemplo [Sidekiq](https://sidekiq.org).
85
+
86
+ Recuerda en producción configurar correctamente `default_url_options` y `asset_host` en tu archivo `config/enviroments/production.rb`.
87
+
88
+ ```ruby
89
+ config.action_mailer.default_url_options = { host: "my-domain.com" }
90
+ config.action_mailer.asset_host = "https://my-domain.com"
91
+ ```
92
+
93
+ En modo de desarrollo es útil contar con una gema como [letter opener](https://github.com/ryanb/letter_opener) que evite el envío de correos electrónicos y que en su lugar los abra en línea. Para este caso agrega la gema a tu `Gemfile` y ejecuta el comando `bundle`.
94
+
95
+ ```ruby
96
+ gem "letter_opener", group: :development
97
+ ```
98
+
99
+ Abre tu archivo `config/enviroments/development.rb` y configurala de la siguiente forma:
100
+
101
+ ```ruby
102
+ config.action_mailer.delivery_method = :letter_opener
103
+ config.action_mailer.perform_deliveries = true
104
+ ```
105
+
106
+ ### Personalización de vistas
107
+ Este paso no es necesario, con los pasos anteriores se obtienen las vistas default de la gema, pero si así lo requieres puedes personalizar las vistas de la gema, utiliza este comando que copia las vistas del engine dentro de tu aplicación. Los archivos se generan dentro de `views/no_password` y `views/layouts/no_password`.
108
+
109
+ ```bash
110
+ $ rails no_password:install:copy_templates
111
+ ```
112
+
113
+ ## Uso de NoPassword
114
+ Después de instalar NoPassword para utilizarla en tu aplicación se proporcionan filtros y **helpers** que te permiten conocer si existe una sesión activa o no, y en su defecto, enviar al usuario a crear una sesión antes de continuar.
115
+
116
+ ### Filtros de controlador
117
+ El filtro `authenticate_session!` se incluye en el módulo `NoPassword::ControllerHelpers`. Este filtro es utilizado en un `before_action` para determinar en un controlador si existe una sesión activa o no. En el caso de no existir, el usuario es redirigido a la sección de NoPassword donde podrá solicitar un token de acceso.
118
+
119
+ Para hacer uso del filtro hay que incluir el módulo en el controlador deseado y agregar el `before_action`.
120
+
121
+ ```ruby
122
+ include NoPassword::ControllerHelpers
123
+ before_action :authenticate_session!
124
+ ```
125
+
126
+ ### Helper methods
127
+ El módulo `NoPassword::ControllerHelpers` incluye los métodos `signed_in_session?` y `current_session` para consultar si existe una sesión activa y obtener más información de la sesión, respectivamente. Cuando el módulo de incluye en un controlador estos métodos se exponen en el contexto de las vistas.
128
+
129
+ Ejemplo de uso en una vista.
130
+
131
+ ```erb
132
+ <% if signed_in_session? %>
133
+ <%= current_session.email %>
134
+ <%= button_to "Sign out", no_password.session_path(current_session.id), method: :delete %>
135
+ <% else %>
136
+ <%= link_to "Sign in", no_password.new_session_path %>
137
+ <% end %>
138
+ ```
139
+
140
+ ### Callback
141
+ El flujo normal para iniciar sesión consta de dos pasos, una página donde se introduce el correo electrónico para solicitar un código y/o el link mágico, otra página donde se introduce el código, sin embargo pueden existir casos que se salgan de esta flujo propuesto por NoPassword. Para situaciones donde se desea forzar a donde dirigir al usuario después de iniciar sesión o cuando el link mágico es inválido, o simular **Single Page Application** tenemos a nuestra disposición el callback `after_sign_in!`.
142
+
143
+ ![Aoorora Demo page](docs/aoorora-demo.gif)
144
+
145
+ Este callback va a ser llamado en cada intento de iniciar sesión, ya sea con código o con link mágico, independientemente de si el inicio de sesión es exitoso o no.
146
+ ```ruby
147
+ after_sign_in!(signed_in, by_url, return_url)
148
+ ```
149
+ El callback recibe 3 parámetros.
150
+ - `signed_in`: indica si fue exitoso el inicio de sesión o no, su valor es booleano.
151
+ - `by_url`: indica como se inentó iniciar sesión, ya sea por el link mágico o con el código introducido manualmente, su valor es booleano.
152
+ - `return_url`: Contiene la URL a donde redireccionar al usuario en caso de que el inicio de sesión sea exitoso, su valor es una cadena de texto.
153
+
154
+ El controlador `SessionConfirmationsController` espera como respuesta del callback los siguientes posibles valores:
155
+ - `nil`: con el cual se indica que se ejecutó el callback y que regresa el control del flujo al controlador.
156
+ - `render` o `redirect_to`: en este caso le indicamos al controlador que el callback toma el control del flujo.
157
+
158
+ Podemos implementar el callback `after_sign_in!` creando el archivo `app/controllers/no_password/session_confirmations_controller.rb` en nuestra aplicación principal,
159
+ donde cargamos el controlador original desde el engine de NoPassword y con `class_eval` le inyectamos el método.
160
+
161
+ ```ruby
162
+ load NoPassword::Engine.root.join("app", "controllers", "no_password", "session_confirmations_controller.rb")
163
+
164
+ NoPassword::SessionConfirmationsController.class_eval do
165
+ def after_sign_in!(signed_in, by_url)
166
+ return do_something_different if signed_in # Do something different if user signed in successfully
167
+ return nil if !by_url # Return control if failed to sign in with magic link
168
+
169
+ flash[:alert] = "Your code is not valid"
170
+ redirect_to main_app.demo_path # Redirect somewhere else if token is invalid
171
+ end
172
+ end
173
+ ```
174
+
175
+ ## Generadores
176
+ La gema NoPassword cuenta con 4 generadores de Ruby on Rails, el primero de ellos es el que se encarga de realizar la instalación y realiza las siguientes acciones.
177
+
178
+ - Crea el archivo de inicialización
179
+ - Monta el engine en las rutas de aplicación
180
+ - Incluye los **helpers** de la gema en `ApplicationController`.
181
+ - Copia las migraciones de NoPassword a la aplicación.
182
+ - Genera el archivo de CSS de la gema.
183
+
184
+ ```bash
185
+ $ rails no_password:install
186
+ ```
187
+ Para los casos de requerir personalizar las vistas de la gema, el generador `copy_templates` se encarga de copiar **layouts**, vistas y plantillas de **mailers** a `app/views`, colocando los archivos donde corresponden bajo el directorio `no_password`.
188
+
189
+ ```bash
190
+ $ rails no_password:install:copy_templates
191
+ ```
192
+
193
+ El generador `migrations` se encarga de copiar las migraciones desde la gema hacia la aplicación.
194
+
195
+ ```bash
196
+ $ rails no_password:install:migrations
197
+ ```
198
+
199
+ Los generadores `tailwindcss::build` y `tailwindcss:watch` trabajan en conjunto para generar el CSS basado en Tailwind desde la gema y exponerlo a la aplicación, en ambos casos se generar un archivo `tailwind.config.js` específico para la gema y que Tailwind utiliza para generar el CSS necesario.
200
+ ```bash
201
+ $ rails no_password:tailwindcss:build
202
+ ```
203
+ Este generador `tailwindcss::build` se conecta automática al proceso de compilación de assets en modo de producción.
204
+
205
+ ```bash
206
+ $ rails no_password:tailwindcss:watch
207
+ ```
208
+ El generador `tailwindcss:watch` se usa en modo de desarrollo y se puede agregar al `Procfile.dev` ejecutar el proceso y monitorear posibles cambios al CSS de la gema.
209
+
210
+ ```ruby
211
+ no_password_css: bin/rails app:no_password:tailwindcss:watch
212
+ ```
213
+
214
+ ## Desarrollo y pruebas de la gema
215
+ Para generar un ambiente de desarrollo de la gema, primero es necesario clonar este repositorio. Una vez clonado, el script `bin/setup` se encarga de instalar la dependencias y migrar la base de datos. Además supone que [Overmind](https://github.com/DarthSim/overmind) está instalado por lo que usa la configuración del archivo `Procfile.dev` para iniciar la aplicación de prueba en el puerto 3090.
216
+
217
+ ```bash
218
+ $ bin/setup
219
+ ```
220
+
221
+ ![Aoorora Demo page](docs/dummy-app.png)
222
+
223
+ Para ejecutar la pruebas de la gema y ejecutar los linters se puede hacer uso del script `bin/ci`.
224
+
225
+ ```bash
226
+ $ bin/ci
227
+ ```
228
+
229
+ ## Licencia
230
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/README.md ADDED
@@ -0,0 +1,230 @@
1
+ # NoPassword
2
+
3
+ NoPassword is a Ruby on Rails gem that allows session authentication with a token or a magic link via an email sent to the user; there is no need for a password.
4
+
5
+ ## Table of contents
6
+
7
+ - [Requirements](#requirements)
8
+ - [Installation](#instalation)
9
+ - [Configuration](#configuration)
10
+ - [Initializer](#initializer)
11
+ - [Locales](#locales)
12
+ - [Email configuration](#email-configuration)
13
+ - [Custom views](#custom-views)
14
+ - [Usage](#usage)
15
+ - [Action filter](#action-filter)
16
+ - [Helper methods](#helper-methods)
17
+ - [Callback](#callback)
18
+ - [Generators](#generator)
19
+ - [Develooment and tests](#development-and-tests)
20
+ - [License](#license)
21
+
22
+ ## Requirements
23
+
24
+ NoPassword is a Rails Engine that needs Ruby on Rails 7.0 or better and Ruby 3.0.2 o better.
25
+ NoPassword flow is composed of two views for the user to enter an email, and the received token. The engine uses TailwindCSS via [tailwindcss-rails](https://github.com/rails/tailwindcss-rails) and StimulusJS served with the browser's Importmaps.
26
+
27
+ Customization for CSS and Javascript is possible; refer to [Views personalization](#views-personalization) to learn how to extract gem templates.
28
+
29
+ ## Installation
30
+
31
+ Add NoPassword gem reference to your application's `Gemfile`.
32
+ ```ruby
33
+ gem "no_password_auth", require: "no_password"
34
+ ```
35
+ Or add gem reference from its Github repository.
36
+ ```ruby
37
+ gem "no_password_auth", git: "https://github.com/creditario/nopassword.git", require: "no_password"
38
+ ```
39
+
40
+ In any case, execute `bundle` to install the gem.
41
+ Next, use the gem installer to install migrations and the gem initializer.
42
+
43
+ ```bash
44
+ $ bin/rails no_password:install
45
+ ```
46
+ Don't forget to execute database migration after this step.
47
+ ```bash
48
+ $ bin/rails db:migrate
49
+ ```
50
+
51
+ The following section explains configuration options for the NoPassword gem with its initializer file.
52
+
53
+ ## Configuration
54
+ After installing the NoPassword gem, the engine is mounted at the `/p` path inside the `config/routes.rb` file.
55
+
56
+ ### Initializer
57
+ After installing the NoPassword gem, the engine is mounted at the `/p` path inside the `config/routes.rb` file.
58
+
59
+ NoPassword initializer is located at `config/initializers/no_password.rb`. The gem provides three configuration options. Session expiration by default is set to two hours after a new session is started. To adjust this time, use `session_expiration` attribute to set a new value.
60
+ Token expiration is set by default to 15 minutes. Update `token_expiration` increase or decrease this time.
61
+
62
+ `secret_key` is the key used to sign magic links URLs; if this value is `nil`, then Ruby on Rails `secret_key` is used for this cryptographic task.
63
+
64
+ ```ruby
65
+ NoPassword.configure do |config|
66
+ # Session expiration time
67
+ # config.session_expiration = 2.hours
68
+ #
69
+ # Token expiration time
70
+ # config.token_expiration = 15.minutes
71
+ #
72
+ # Secret key to cypher tokens, if none, then Rails secret key is used
73
+ # config.secret_key = nil
74
+ end
75
+ ```
76
+
77
+ ### Locales
78
+ NoPassword gem includes locales for Spanish and English languages. Set your language configuration at the main application `config/application.rb`.
79
+
80
+ ```ruby
81
+ config.i18n.default_locale = :es
82
+ config.i18n.available_locales = [:en, :es]
83
+ ```
84
+
85
+ ### Email configuration
86
+ NoPassword uses ActionMailer to email the user its token and magic link. If a background strategy is setup for ActiveJob, like [Sidekiq](https://sidekiq.org) then emails are sent in the background.
87
+
88
+ Remember to configure `default_url_options` and `asset_host` inside `config/enviroments/production.rb` file.
89
+
90
+ ```ruby
91
+ config.action_mailer.default_url_options = { host: "my-domain.com" }
92
+ config.action_mailer.asset_host = "https://my-domain.com"
93
+ ```
94
+
95
+ In development mode, having a tool like [letter opener](https://github.com/ryanb/letter_opener) helps to open emails inline, preventing sending emails. Add letter opener gem reference to your `Gemfile` and run `bundle` to install it.
96
+
97
+ ```ruby
98
+ gem "letter_opener", group: :development
99
+ ```
100
+
101
+ Configure the letter opener by opening `config/enviroments/development.rb` and adding the following configuration.
102
+
103
+ ```ruby
104
+ config.action_mailer.delivery_method = :letter_opener
105
+ config.action_mailer.perform_deliveries = true
106
+ ```
107
+
108
+ ### Custom views
109
+ This step is necessary only if you plan to customize the views provided by NoPassword. The following generator copies all engine views into `views/no_password` and `views/layouts/no_password`.
110
+
111
+ ```bash
112
+ $ rails no_password:install:copy_templates
113
+ ```
114
+
115
+ ## Usage
116
+ After installing NoPassword, you can use the filters and **helpers** provided by the gem to query if there is an active session or to send the user to the sign in process.
117
+
118
+ ### Action filter
119
+ The filter `authenticate_session!` is defined when the module `NoPassword::ControllerHelpers` is included in a controller. This filter is used with a `before_action` to secure a controller or controller action. When a user reaches a controller protected by this filter, and there is no active session, the user is redirected to the sign-in process.
120
+
121
+ ```ruby
122
+ include NoPassword::ControllerHelpers
123
+ before_action :authenticate_session!
124
+ ```
125
+
126
+ ### Helper methods
127
+ The module `NoPassword::ControllerHelpers` includes methods `signed_in_session?` and `current_session` to query if there is an active session or to get more information about an active session. These methods are also exposed as helpers in the Rails view context.
128
+
129
+ ```erb
130
+ <% if signed_in_session? %>
131
+ <%= current_session.email %>
132
+ <%= button_to "Sign out", no_password.session_path(current_session.id), method: :delete %>
133
+ <% else %>
134
+ <%= link_to "Sign in", no_password.new_session_path %>
135
+ <% end %>
136
+ ```
137
+
138
+ ### Callback
139
+ NoPassword sign in flow has two pages; the first one receives the user email, creates a token and a magic link, and then sends it via email. The second page accepts the token and decides whether the user can have a session.
140
+ However, an application may require a different flow, which is why the callback `after_sign_in!` exists.
141
+
142
+ This is an example of a custom flow that mimics a Single Page flow.
143
+
144
+ ![Aoorora Demo page](docs/aoorora-demo.gif)
145
+
146
+ The callback is called on every intent to start a session, whether the sign was successful or not.
147
+ ```ruby
148
+ after_sign_in!(signed_in, by_url, return_url)
149
+ ```
150
+ It receives three parameters.
151
+ - `signed_in`: A boolean value that indicates if the user succeeded in getting a session.
152
+ - `by_url`: A boolean value that indicates if the login happens with the magic link or entered token manually.
153
+ - `return_url`: A string value with the return path if the user succeeded in getting a session.
154
+
155
+ The `SessionConfirmationsController` controller expects any of the following possible values from the callback.
156
+ - `nil`: indicates callback was executed but is returning flow control to the controller.
157
+ - `render` o `redirect_to`: indicates callback was executed and is taking over sign in flow.
158
+
159
+ `after_sign_in!` callback is implemented by creating a `app/controllers/no_password/session_confirmations_controller.rb` file in your application. The original controller from NoPassword engine is loaded, and then the callback is added with a `class_eval`.
160
+
161
+ ```ruby
162
+ load NoPassword::Engine.root.join("app", "controllers", "no_password", "session_confirmations_controller.rb")
163
+
164
+ NoPassword::SessionConfirmationsController.class_eval do
165
+ def after_sign_in!(signed_in, by_url)
166
+ return do_something_different if signed_in # Do something different if user signed in successfully
167
+ return nil if !by_url # Return control if failed to sign in with magic link
168
+
169
+ flash[:alert] = "Your code is not valid"
170
+ redirect_to main_app.demo_path # Redirect somewhere else if token is invalid
171
+ end
172
+ end
173
+ ```
174
+
175
+ ## Generators
176
+ NoPassword gem has four Ruby on Rails generators. The first one is the installer which performs the following actions.
177
+
178
+ - Creates the gem initializer.
179
+ - Mount the engine in the routes file.
180
+ - Adds the gem Helpers into `ApplicationController`.
181
+ - Copy NoPassword migrations to the application.
182
+ - Generates the Tailwind config file and generates de CSS for NoPassword.
183
+
184
+ ```bash
185
+ $ rails no_password:install
186
+ ```
187
+ `copy_templates` generator copies gem templates to the application to allow views customization. Files are copied to `app/views` inside a `no_password` folder.
188
+
189
+ ```bash
190
+ $ rails no_password:install:copy_templates
191
+ ```
192
+
193
+ `migrations` generator copies the gem migrations to the application.
194
+
195
+ ```bash
196
+ $ rails no_password:install:migrations
197
+ ```
198
+
199
+ `tailwindcss::build` and `tailwindcss:watch` generators work together to generate de Tailwind CSS for gem views. Both generate a `tailwind.config.js` file that needs to be added to `.gitignore` file.
200
+ ```bash
201
+ $ rails no_password:tailwindcss:build
202
+ ```
203
+ `tailwindcss::build` generator connects automatically to the assets compilation process in production to generate the Tailwind CSS for the gem.
204
+
205
+ ```bash
206
+ $ rails no_password:tailwindcss:watch
207
+ ```
208
+ `tailwindcss:watch` generator is used in development to generate the Tailwind CSS for the gem. Add it to `Procfile.dev` file to ensure that CSS is generated in development when using Foreman or Overmind.
209
+
210
+ ```ruby
211
+ no_password_css: bin/rails app:no_password:tailwindcss:watch
212
+ ```
213
+
214
+ ## Development and tests
215
+ A development environment for this gem is easy to create, clone the repository and run the script `bin/setup` to install dependencies and prepare a database (Postgresql). The script assumes that you have [Overmind](https://github.com/DarthSim/overmind) installed, which used the `Procfile.dev` to start the sample application on port 3090.
216
+
217
+ ```bash
218
+ $ bin/setup
219
+ ```
220
+
221
+ ![Aoorora Demo page](docs/dummy-app.png)
222
+
223
+ To execute the gem tests, use the script `bin/ci`, which also runs the linters.
224
+
225
+ ```bash
226
+ $ bin/ci
227
+ ```
228
+
229
+ ## License
230
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
9
+
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << "test"
14
+ t.pattern = "test/**/*_test.rb"
15
+ t.verbose = false
16
+ end
17
+
18
+ task default: :test
@@ -0,0 +1,4 @@
1
+ //= link_directory ../../stylesheets/no_password .css
2
+ //= link_tree ../../images/no_password
3
+
4
+ //= link_tree ../../javascripts/no_password .js
@@ -0,0 +1,61 @@
1
+ const defaultTheme = require('tailwindcss/defaultTheme')
2
+
3
+ function withOpacityValue(variableName) {
4
+ return ({opacityValue}) => {
5
+ opacityValue = opacityValue ?? 1;
6
+ return `rgba(var(${variableName}), ${opacityValue})`
7
+ }
8
+ };
9
+
10
+ module.exports = {
11
+ content: [
12
+ '/Users/marioch/Development/creditario/nopassword/app/views/**/*',
13
+ '/Users/marioch/Development/creditario/nopassword/app/helpers/**/*',
14
+ '/Users/marioch/Development/creditario/nopassword/app/controllers/**/*',
15
+ '/Users/marioch/Development/creditario/nopassword/app/javascript/**/*.js',
16
+ '/Users/marioch/Development/creditario/nopassword/app/assets/**/application.tailwind.css'
17
+ ],
18
+ theme: {
19
+ extend: {
20
+ fontFamily: {
21
+ sans: ['Inter var', ...defaultTheme.fontFamily.sans],
22
+ },
23
+ textColor: {
24
+ skin: {
25
+ inverted: withOpacityValue('--color-inverted'),
26
+ accented: withOpacityValue('--color-accented'),
27
+ 'accented-hover': withOpacityValue('--color-accented-hover'),
28
+ base: withOpacityValue('--color-base'),
29
+ muted: withOpacityValue('--color-muted'),
30
+ dimmed: withOpacityValue('--color-dimmed'),
31
+ error: withOpacityValue('--color-error'),
32
+ }
33
+ },
34
+ backgroundColor: {
35
+ skin: {
36
+ 'button-accented': withOpacityValue('--color-accented'),
37
+ 'button-accented-hover': withOpacityValue('--color-accented-hover'),
38
+ 'button-inverted': withOpacityValue('--color-inverted'),
39
+ 'button-inverted-hover': withOpacityValue('--color-inverted-hover'),
40
+ muted: withOpacityValue('--color-muted'),
41
+ dimmed: withOpacityValue('--color-dimmed'),
42
+ accent: withOpacityValue('--color-accent'),
43
+ }
44
+ },
45
+ ringColor: {
46
+ skin: {
47
+ accented: withOpacityValue('--color-border-accented'),
48
+ }
49
+ },
50
+ borderColor: {
51
+ skin: {
52
+ base: withOpacityValue('--color-border-base'),
53
+ accented: withOpacityValue('--color-border-accented'),
54
+ }
55
+ }
56
+ },
57
+ },
58
+ plugins: [
59
+ require('@tailwindcss/forms')
60
+ ],
61
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 343.07 89.83"><defs><style>.cls-1{fill:#504689;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M148.41,29.06h6.85V60.77h-6.85V56.2c-2.6,3.62-6.28,5.39-11.1,5.39a14.83,14.83,0,0,1-11.16-4.82,16.49,16.49,0,0,1-4.63-11.85,16.39,16.39,0,0,1,4.63-11.86,14.83,14.83,0,0,1,11.16-4.82c4.82,0,8.5,1.77,11.1,5.32Zm-10,26a9.79,9.79,0,0,0,7.17-2.85,10.06,10.06,0,0,0,2.85-7.29,9.84,9.84,0,0,0-2.85-7.24,10.29,10.29,0,0,0-14.34,0,9.84,9.84,0,0,0-2.85,7.24,10.06,10.06,0,0,0,2.85,7.29A9.79,9.79,0,0,0,138.39,55.06Z"/><path class="cls-1" d="M165,56.77a16.12,16.12,0,0,1-4.88-11.85,16.74,16.74,0,1,1,33.48,0A16.82,16.82,0,0,1,165,56.77Zm11.86-1.84a9.57,9.57,0,0,0,7-2.85,9.75,9.75,0,0,0,2.85-7.16,9.75,9.75,0,0,0-2.85-7.17,10.09,10.09,0,0,0-14.08,0A9.75,9.75,0,0,0,167,44.92a9.75,9.75,0,0,0,2.85,7.16A9.57,9.57,0,0,0,176.86,54.93Z"/><path class="cls-1" d="M201.51,56.77a16.12,16.12,0,0,1-4.88-11.85,16.75,16.75,0,1,1,33.49,0,16.82,16.82,0,0,1-28.61,11.85Zm11.86-1.84a9.57,9.57,0,0,0,7-2.85,9.76,9.76,0,0,0,2.86-7.16,9.76,9.76,0,0,0-2.86-7.17,10.09,10.09,0,0,0-14.08,0,9.75,9.75,0,0,0-2.85,7.17,9.75,9.75,0,0,0,2.85,7.16A9.57,9.57,0,0,0,213.37,54.93Z"/><path class="cls-1" d="M241.79,34.39c1.71-3.94,5-5.9,9.76-5.9v7.42a9.91,9.91,0,0,0-6.85,1.9c-2,1.4-2.91,3.74-2.91,7v16h-6.85V29.06h6.85Z"/><path class="cls-1" d="M257.83,56.77a16.09,16.09,0,0,1-4.89-11.85,16.75,16.75,0,1,1,33.49,0,16.82,16.82,0,0,1-28.6,11.85Zm11.85-1.84a9.55,9.55,0,0,0,7-2.85,9.72,9.72,0,0,0,2.86-7.16,9.72,9.72,0,0,0-2.86-7.17,10.08,10.08,0,0,0-14.07,0,9.72,9.72,0,0,0-2.86,7.17,9.72,9.72,0,0,0,2.86,7.16A9.55,9.55,0,0,0,269.68,54.93Z"/><path class="cls-1" d="M298.1,34.39c1.71-3.94,5-5.9,9.77-5.9v7.42a9.88,9.88,0,0,0-6.85,1.9c-2,1.4-2.92,3.74-2.92,7v16h-6.85V29.06h6.85Z"/><path class="cls-1" d="M336.22,29.06h6.85V60.77h-6.85V56.2c-2.6,3.62-6.28,5.39-11.1,5.39A14.81,14.81,0,0,1,314,56.77a16.49,16.49,0,0,1-4.63-11.85A16.39,16.39,0,0,1,314,33.06a14.81,14.81,0,0,1,11.16-4.82c4.82,0,8.5,1.77,11.1,5.32Zm-10,26a9.75,9.75,0,0,0,7.16-2.85,10,10,0,0,0,2.86-7.29,9.8,9.8,0,0,0-2.86-7.24,10.28,10.28,0,0,0-14.33,0,9.84,9.84,0,0,0-2.85,7.24A10.06,10.06,0,0,0,319,52.21,9.75,9.75,0,0,0,326.2,55.06Z"/><path class="cls-1" d="M35,31.82l.1-.2A5,5,0,0,1,39.5,29H50.32a5,5,0,0,1,4.42,2.67l.1.2a8.89,8.89,0,0,0,7.85,4.74H89a45.48,45.48,0,0,0-1.6-6.12H57.27a5,5,0,0,1-4.42-2.67A9.14,9.14,0,0,0,46.62,23a8.3,8.3,0,0,0-1.71-.15,8.39,8.39,0,0,0-1.72.15A9.1,9.1,0,0,0,37,27.77a5,5,0,0,1-4.42,2.67H2.39A44.06,44.06,0,0,0,.78,36.56H27.13A8.86,8.86,0,0,0,35,31.82Z"/><path class="cls-1" d="M42.36,18.08a3.63,3.63,0,0,1,5.09,0,12.13,12.13,0,0,0,8.66,3.26h27a45.46,45.46,0,0,0-4.53-6.12H11.22a45.46,45.46,0,0,0-4.53,6.12h27A12.11,12.11,0,0,0,42.36,18.08Z"/><path class="cls-1" d="M44.91,89.83a44.73,44.73,0,0,0,22.63-6.12H22.28A44.67,44.67,0,0,0,44.91,89.83Z"/><path class="cls-1" d="M44.91,0A44.67,44.67,0,0,0,22.28,6.12H67.54A44.73,44.73,0,0,0,44.91,0Z"/><path class="cls-1" d="M65.25,45.66A5,5,0,0,1,60.82,43l-.1-.19a8.83,8.83,0,0,0-7.82-4.74h-16a8.85,8.85,0,0,0-7.83,4.74L29,43a5,5,0,0,1-4.42,2.68H0a45.56,45.56,0,0,0,.53,6.12H19.17A8.83,8.83,0,0,0,27,47l.1-.2a5,5,0,0,1,4.42-2.67h26.8a5,5,0,0,1,4.41,2.67l.1.2a8.84,8.84,0,0,0,7.83,4.74H89.29a45.54,45.54,0,0,0,.52-6.12Z"/><path class="cls-1" d="M68.8,58.21l-.1-.2a8.85,8.85,0,0,0-7.83-4.74H29A8.86,8.86,0,0,0,21.11,58l-.1.2a5,5,0,0,1-4.42,2.67H2.93A44.22,44.22,0,0,0,5.8,67h5.38A8.85,8.85,0,0,0,19,62.26l.11-.2a5,5,0,0,1,4.42-2.67H66.28a5,5,0,0,1,4.42,2.67l.1.2A8.86,8.86,0,0,0,78.64,67H84a45.47,45.47,0,0,0,2.88-6.12H73.22A5,5,0,0,1,68.8,58.21Z"/><path class="cls-1" d="M67,68.49H22.79c-4.5,0-6.89,0-10.7,6.12H77.72C73.91,68.49,71.53,68.49,67,68.49Z"/></g></g></svg>
@@ -0,0 +1,4 @@
1
+ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2
+ import "@hotwired/turbo-rails"
3
+
4
+ import "controllers"
@@ -0,0 +1,31 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { useTransition } from "stimulus-use"
3
+
4
+ /**
5
+ * Alert Controller
6
+ *
7
+ * Este controlador funciona con el template _notification.html.erb que es utilizado
8
+ * en el layout NoPassword.
9
+ * Cuando hay información en el objeto Flash de Rails se muestra con una animación
10
+ * el mensaje y desaparece solo después de 10 segundos o cerrándolo con la X.
11
+ */
12
+
13
+ export default class extends Controller {
14
+ static targets = ["notification"];
15
+
16
+ connect() {
17
+ let element = this.element;
18
+ if (this.hasNotificationTarget) {
19
+ element = this.notificationTarget
20
+ }
21
+ useTransition(this, { element: element })
22
+
23
+ this.enter()
24
+
25
+ setTimeout(this.close.bind(this), 10000)
26
+ }
27
+
28
+ close() {
29
+ this.leave()
30
+ }
31
+ }
@@ -0,0 +1,10 @@
1
+ import { Application } from "@hotwired/stimulus"
2
+
3
+ const application = Application.start()
4
+
5
+ // Configure Stimulus development experience
6
+ application.warnings = true
7
+ application.debug = false
8
+ window.Stimulus = application
9
+
10
+ export { application }
@@ -0,0 +1,11 @@
1
+ // Import and register all your controllers from the importmap under controllers/*
2
+
3
+ import { application } from "controllers/application"
4
+
5
+ // Eager load all controllers defined in the import map under controllers/**/*_controller
6
+ import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
7
+ eagerLoadControllersFrom("controllers", application)
8
+
9
+ // Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
10
+ // import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
11
+ // lazyLoadControllersFrom("controllers", application)
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */