graphql-auth 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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