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 +4 -4
- data/README.md +10 -0
- data/app/graphql/mutations/auth/forgot_password.rb +9 -3
- data/app/graphql/mutations/auth/reset_password.rb +11 -8
- data/app/graphql/mutations/auth/sign_in.rb +22 -7
- data/app/graphql/mutations/auth/sign_up.rb +18 -7
- data/app/graphql/mutations/auth/update_account.rb +17 -9
- data/app/graphql/mutations/auth/validate_token.rb +9 -4
- data/app/helpers/graphql/auth_helper.rb +24 -6
- data/app/helpers/graphql/token_helper.rb +23 -0
- data/lib/generators/graphql_auth/install_generator.rb +1 -0
- data/lib/generators/graphql_auth/templates/add_refresh_token_to_user.rb +5 -0
- data/lib/graphql-auth/jwt_manager.rb +12 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b217f15c97c13f82c9a46e990b43d3330d188c1e581858eebf950cb002d4a4fd
|
4
|
+
data.tar.gz: 16c2e73bb8afd958b1c1ea1c13976d5c3feca2af34a58ae2cbed4545bbc67b82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 :
|
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
|
-
{
|
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
|
-
#
|
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 :
|
27
|
-
field :
|
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
|
-
|
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
|
-
{
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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 :
|
29
|
-
field :
|
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
|
-
|
37
|
-
|
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 :
|
29
|
-
field :
|
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
|
-
{
|
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
|
-
|
15
|
-
|
18
|
+
|
16
19
|
def resolve
|
17
20
|
user = context[:current_user]
|
18
|
-
|
21
|
+
|
19
22
|
{
|
20
|
-
|
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
|
23
|
+
user = User.find_by id: user_id
|
21
24
|
|
22
25
|
# update token if user is found with token
|
23
|
-
|
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
|
@@ -8,19 +8,28 @@ module GraphQL
|
|
8
8
|
TYPE = 'Bearer'
|
9
9
|
|
10
10
|
class << self
|
11
|
-
def
|
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.
|
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:
|
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
|
-
|
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
|