graphql-auth 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ffa56e33c28af702f87bb776c192921f83086b070fb05d5abf98eefb73c9787
4
- data.tar.gz: 8ad60b751d4c1c3438d780a145692f557b3d6624698fe8d20f0ab8917d2e57c1
3
+ metadata.gz: b217f15c97c13f82c9a46e990b43d3330d188c1e581858eebf950cb002d4a4fd
4
+ data.tar.gz: 16c2e73bb8afd958b1c1ea1c13976d5c3feca2af34a58ae2cbed4545bbc67b82
5
5
  SHA512:
6
- metadata.gz: 5bd40850c4273a5b071e9e9b7b46afcaa7ec5bf9a9bc8d1774f76767832e33a5fdb6d90f4438c3c543be50a344fefbde4e0c5d4b49951912e3708dcdb6372275
7
- data.tar.gz: 2c1be37cece9f12dfa1fd57812ba7c66a0d893f1fe1d570e404a2eea5c510f128e09a7df7a9be8ef949de0a964d8fb70a379b4a134915e3196a74ebd47ef9744
6
+ metadata.gz: 4e80d69d1268094b93c0bab8e0f4c8330b36e11cb3deefedef20dc03a1593960de962a74de2f5cdbec006c6f0ea561297fc5b31e41e86befdff5619ddff8313c
7
+ data.tar.gz: 4ef5135a028e31897b0a446197135e97a1f31ef73e51d160fcab572932c3f0d3e207a981bdf39a60a7efa5aa44dc1711040b040a321a4025a816b83cf61d1969
data/README.md CHANGED
@@ -26,6 +26,16 @@ rails g graphql_auth:install
26
26
 
27
27
  Make sure to read all configurations present inside the file and fill them with your own configs.
28
28
 
29
+ ## Devise gem
30
+
31
+ Use Devise with a User model and skip all route
32
+
33
+ ```
34
+ Rails.application.routes.draw do
35
+ devise_for :users, skip: :all
36
+ end
37
+ ```
38
+
29
39
  ## Usage
30
40
 
31
41
  Make 'JWT_SECRET_KEY' and 'APP_URL' available to ENV
@@ -3,6 +3,7 @@
3
3
  # mutation {
4
4
  # forgotPassword(email: "email@example.com") {
5
5
  # valid
6
+ # success
6
7
  # }
7
8
  # }
8
9
 
@@ -11,12 +12,17 @@ class Mutations::Auth::ForgotPassword < GraphQL::Schema::Mutation
11
12
  description 'The email with forgotten password'
12
13
  end
13
14
 
14
- field :valid, Boolean, null: false
15
-
15
+ field :errors, [::Types::Auth::Error], null: false
16
+ field :success, Boolean, null: false
17
+
16
18
  def resolve(email:)
17
19
  user = User.find_by email: email
18
20
  user.send_reset_password_instructions if user.present?
19
21
 
20
- { valid: true }
22
+ {
23
+ errors: [],
24
+ success: true,
25
+ valid: true
26
+ }
21
27
  end
22
28
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # mutation {
4
4
  # resetPassword(resetPasswordToken: "token", password: "password", passwordConfirmation: "password") {
5
- # valid
5
+ # success
6
6
  # errors {
7
7
  # field
8
8
  # message
@@ -18,20 +18,20 @@ class Mutations::Auth::ResetPassword < GraphQL::Schema::Mutation
18
18
  argument :password, String, required: true do
19
19
  description "New user's new password"
20
20
  end
21
-
21
+
22
22
  argument :password_confirmation, String, required: true do
23
23
  description "New user's new password confirmation"
24
24
  end
25
25
 
26
- field :valid, Boolean, null: false
27
- field :errors, [Types::Auth::Error], null: true
28
-
26
+ field :errors, [::Types::Auth::Error], null: false
27
+ field :success, Boolean, null: false
28
+
29
29
  def resolve(args)
30
30
  user = User.reset_password_by_token args
31
-
31
+
32
32
  if user.errors.any?
33
33
  {
34
- valid: false,
34
+ success: true,
35
35
  errors: user.errors.messages.map do |field, messages|
36
36
  field = field == :reset_password_token ? :_error : field.to_s.camelize(:lower)
37
37
  {
@@ -40,7 +40,10 @@ class Mutations::Auth::ResetPassword < GraphQL::Schema::Mutation
40
40
  end
41
41
  }
42
42
  else
43
- { valid: true }
43
+ {
44
+ errors: [],
45
+ success: true
46
+ }
44
47
  end
45
48
  end
46
49
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  # mutation {
4
4
  # signIn(email: "email@example.com", password: "password") {
5
+ # success
5
6
  # user {
6
7
  # email
7
8
  # }
@@ -13,6 +14,8 @@
13
14
  # }
14
15
 
15
16
  class Mutations::Auth::SignIn < GraphQL::Schema::Mutation
17
+ include ::Graphql::TokenHelper
18
+
16
19
  argument :email, String, required: true do
17
20
  description "The user's email"
18
21
  end
@@ -21,17 +24,27 @@ class Mutations::Auth::SignIn < GraphQL::Schema::Mutation
21
24
  description "The user's password"
22
25
  end
23
26
 
24
- field :user, Types::Auth::User, null: true
25
- field :errors, [Types::Auth::Error], null: true
26
-
27
- def resolve(email:, password:)
27
+ argument :remember_me, Boolean, required: true do
28
+ description "User's checkbox to be remembered after connection timeout"
29
+ end
30
+
31
+ field :errors, [::Types::Auth::Error], null: false
32
+ field :success, Boolean, null: false
33
+ field :user, ::Types::Auth::User, null: true
34
+
35
+ def resolve(email:, password:, remember_me:)
28
36
  response = context[:response]
37
+
29
38
  user = User.find_by email: email
30
39
  valid_sign_in = user.present? && user.valid_password?(password)
31
-
40
+
32
41
  if valid_sign_in
33
- response.set_header 'Authorization', GraphQL::Auth::JwtManager.issue({ user: user.id }) # TODO use uuid
42
+ generate_access_token(user, response)
43
+ remember_me ? set_refresh_token(user, response) : delete_refresh_token(user)
44
+
34
45
  {
46
+ errors: [],
47
+ success: true,
35
48
  user: user
36
49
  }
37
50
  else
@@ -42,7 +55,9 @@ class Mutations::Auth::SignIn < GraphQL::Schema::Mutation
42
55
  message: I18n.t('devise.failure.invalid',
43
56
  authentication_keys: I18n.t('activerecord.attributes.user.email'))
44
57
  }
45
- ]
58
+ ],
59
+ success: false,
60
+ user: nil,
46
61
  }
47
62
  end
48
63
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  # mutation {
4
4
  # signUp(email: "email@example.com", password: "password", passwordConfirmation: "password") {
5
+ # success
5
6
  # user {
6
7
  # email
7
8
  # }
@@ -13,6 +14,8 @@
13
14
  # }
14
15
 
15
16
  class Mutations::Auth::SignUp < GraphQL::Schema::Mutation
17
+ include ::Graphql::TokenHelper
18
+
16
19
  argument :email, String, required: true do
17
20
  description "New user's email"
18
21
  end
@@ -25,21 +28,29 @@ class Mutations::Auth::SignUp < GraphQL::Schema::Mutation
25
28
  description "New user's password confirmation"
26
29
  end
27
30
 
28
- field :user, Types::Auth::User, null: true
29
- field :errors, [Types::Auth::Error], null: true
30
-
31
+ field :errors, [::Types::Auth::Error], null: false
32
+ field :success, Boolean, null: false
33
+ field :user, ::Types::Auth::User, null: true
34
+
31
35
  def resolve(args)
32
36
  response = context[:response]
33
37
  user = User.new args
34
-
38
+
35
39
  if user.save
36
- response.set_header 'Authorization', GraphQL::Auth::JwtManager.issue({ user: user.id }) # TODO use uuid
37
- { user: user }
40
+ generate_access_token(user, response)
41
+
42
+ {
43
+ errors: [],
44
+ success: true,
45
+ user: user
46
+ }
38
47
  else
39
48
  {
40
49
  errors: user.errors.messages.map do |field, messages|
41
50
  { field: field.to_s.camelize(:lower), message: messages.first.capitalize }
42
- end
51
+ end,
52
+ success: false,
53
+ user: nil,
43
54
  }
44
55
  end
45
56
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  # mutation {
4
4
  # updateAccount(current_password: "currentPassword", password: "newPassword", password_confirmation: "newPassword") {
5
+ # success
5
6
  # user {
6
7
  # email
7
8
  # }
@@ -16,30 +17,37 @@ class Mutations::Auth::UpdateAccount < GraphQL::Schema::Mutation
16
17
  argument :current_password, String, required: true do
17
18
  description "User's current password"
18
19
  end
19
-
20
+
20
21
  argument :password, String, required: true do
21
22
  description "User's new password"
22
23
  end
23
-
24
+
24
25
  argument :password_confirmation, String, required: true do
25
26
  description "User's new password confirmation"
26
27
  end
27
-
28
- field :user, Types::Auth::User, null: true
29
- field :errors, [Types::Auth::Error], null: true
30
-
28
+
29
+ field :errors, [::Types::Auth::Error], null: false
30
+ field :success, Boolean, null: false
31
+ field :user, ::Types::Auth::User, null: true
32
+
31
33
  def resolve(args)
32
34
  user = context[:current_user]
33
35
  user.update_with_password args
34
-
36
+
35
37
  if user.errors.any?
36
38
  {
37
39
  errors: user.errors.messages.map do |field, messages|
38
40
  { field: field.to_s.camelize(:lower), message: messages.first.capitalize }
39
- end
41
+ end,
42
+ success: false,
43
+ user: nil
40
44
  }
41
45
  else
42
- { user: user }
46
+ {
47
+ errors: [],
48
+ success: true,
49
+ user: user
50
+ }
43
51
  end
44
52
  end
45
53
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  # mutation {
4
4
  # validateToken {
5
+ # success
5
6
  # valid
6
7
  # user {
7
8
  # email
@@ -10,15 +11,19 @@
10
11
  # }
11
12
 
12
13
  class Mutations::Auth::ValidateToken < GraphQL::Schema::Mutation
14
+ field :errors, [::Types::Auth::Error], null: false
15
+ field :success, Boolean, null: false
16
+ field :user, ::Types::Auth::User, null: true
13
17
  field :valid, Boolean, null: false
14
- field :user, Types::Auth::User, null: true
15
-
18
+
16
19
  def resolve
17
20
  user = context[:current_user]
18
-
21
+
19
22
  {
20
- valid: user.present?,
23
+ errors: [],
24
+ success: user.present?,
21
25
  user: user,
26
+ valid: user.present?,
22
27
  }
23
28
  end
24
29
  end
@@ -4,27 +4,45 @@
4
4
 
5
5
  module Graphql
6
6
  module AuthHelper
7
+ include ::Graphql::TokenHelper
8
+
7
9
  def context
8
10
  {
9
11
  current_user: current_user,
10
12
  response: response,
11
13
  }
12
14
  end
13
-
15
+
16
+ # set current user from Authorization header
14
17
  def current_user
15
18
  return if request.headers['Authorization'].nil?
16
-
19
+
17
20
  decrypted_token = GraphQL::Auth::JwtManager.decode(request.headers['Authorization'])
18
-
21
+
19
22
  user_id = decrypted_token['user']
20
- user = User.find_by id: user_id # TODO use uuid
23
+ user = User.find_by id: user_id
21
24
 
22
25
  # update token if user is found with token
23
- response.set_header 'Authorization', GraphQL::Auth::JwtManager.issue({ user: user.id }) if user.present?
26
+ if user.present?
27
+ generate_access_token(user, response)
28
+ end
24
29
 
25
30
  user
31
+
32
+ # rescue expired Authorization header with RefreshToken header
26
33
  rescue JWT::ExpiredSignature
27
- nil
34
+ return nil if request.headers['RefreshToken'].nil?
35
+
36
+ user = User.find_by refresh_token: request.headers['RefreshToken']
37
+
38
+ if user.present?
39
+ generate_access_token(user, response)
40
+ set_refresh_token(user, response)
41
+ end
42
+
43
+ user
28
44
  end
45
+
46
+
29
47
  end
30
48
  end
@@ -0,0 +1,23 @@
1
+ # include this helper in GraphqlController to use context method so that current_user will be available
2
+ #
3
+ # ::GraphqlSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
4
+
5
+ module Graphql
6
+ module TokenHelper
7
+ def generate_access_token(user, response)
8
+ token = GraphQL::Auth::JwtManager.issue_with_expiration({ user: user.id }) # TODO use uuid
9
+ response.set_header 'Authorization', token
10
+ response.set_header 'Expires', GraphQL::Auth::JwtManager.token_expiration(token)
11
+ end
12
+
13
+ def set_refresh_token(user, response)
14
+ refresh_token = user.refresh_token.presence || GraphQL::Auth::JwtManager.issue_without_expiration({ user: user.id })
15
+ user.update_column :refresh_token, refresh_token
16
+ response.set_header 'RefreshToken', refresh_token
17
+ end
18
+
19
+ def delete_refresh_token(user)
20
+ user.update_column :refresh_token, nil if user.refresh_token.present?
21
+ end
22
+ end
23
+ end
@@ -5,6 +5,7 @@ module GraphqlAuth
5
5
 
6
6
  def copy_configuration
7
7
  template 'initializer.rb', 'config/initializers/graphql_auth.rb'
8
+ template 'add_refresh_token_to_user.rb', "db/migrate/#{Time.now.strftime('%Y%m%d%H%M%S')}_add_refresh_token_to_user.rb"
8
9
  end
9
10
  end
10
11
  end
@@ -0,0 +1,5 @@
1
+ class AddRefreshTokenToUser < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :users, :refresh_token, :string, default: nil
4
+ end
5
+ end
@@ -8,19 +8,28 @@ module GraphQL
8
8
  TYPE = 'Bearer'
9
9
 
10
10
  class << self
11
- def issue(payload, custom_expiration = nil)
11
+ def issue_with_expiration(payload, custom_expiration = nil)
12
12
  if custom_expiration.present? && custom_expiration.is_a?(ActiveSupport::Duration)
13
13
  payload[:exp] = custom_expiration
14
14
  else
15
15
  payload.merge!(expiration)
16
16
  end
17
17
 
18
+ issue payload
19
+ end
20
+
21
+ def issue(payload)
18
22
  token = JWT.encode payload,
19
23
  auth_secret,
20
24
  ALGORITHM
21
25
  set_type token
22
26
  end
23
27
 
28
+ def token_expiration(token)
29
+ decrypted_token = decode(token)
30
+ decrypted_token.try(:[], 'exp')
31
+ end
32
+
24
33
  def decode(token)
25
34
  token = extract_token token
26
35
  decrypted_token = JWT.decode token,
@@ -48,6 +57,8 @@ module GraphQL
48
57
  exp = Time.now.to_i + GraphQL::Auth.configuration.token_lifespan
49
58
  { exp: exp }
50
59
  end
60
+
61
+ alias_method :issue_without_expiration, :issue
51
62
  end
52
63
  end
53
64
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Ferland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-19 00:00:00.000000000 Z
11
+ date: 2019-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -99,8 +99,10 @@ files:
99
99
  - app/graphql/types/auth/user.rb
100
100
  - app/graphql/types/graphql_auth.rb
101
101
  - app/helpers/graphql/auth_helper.rb
102
+ - app/helpers/graphql/token_helper.rb
102
103
  - app/views/devise/mailer/reset_password_instructions.html.erb
103
104
  - lib/generators/graphql_auth/install_generator.rb
105
+ - lib/generators/graphql_auth/templates/add_refresh_token_to_user.rb
104
106
  - lib/generators/graphql_auth/templates/initializer.rb
105
107
  - lib/graphql-auth.rb
106
108
  - lib/graphql-auth/configuration.rb
@@ -127,8 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
129
  - !ruby/object:Gem::Version
128
130
  version: '0'
129
131
  requirements: []
130
- rubyforge_project:
131
- rubygems_version: 2.7.6
132
+ rubygems_version: 3.0.1
132
133
  signing_key:
133
134
  specification_version: 4
134
135
  summary: GraphQL + JWT + Devise