keratin-authn 0.1.3 → 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
  SHA1:
3
- metadata.gz: 75d95ac6c9ef32e84ecd20f8f2431c8bc95557f0
4
- data.tar.gz: fbd476314716da9dc09efa0c8ad02173902e9c5c
3
+ metadata.gz: 5bc3dc3a5993e496aa7d4a27460d799b4e3ea600
4
+ data.tar.gz: 30266ffdffaa188785ce930f0f470a5cde684746
5
5
  SHA512:
6
- metadata.gz: a051bb2f34386baccab8d922a06fb5e174f61d5030001097872e2d6169092696726e4054e973bdba27e7cc7b36e74481f9ef418455520a30195a48027c3ecfb7
7
- data.tar.gz: ed7623eea06f6736fdc4e828200adfe5c78f76a3a59fd9fd440889a2a48fc84073155da385bba01951ff0161bae983c48609efaea868db3cf8041a171856bd2a
6
+ metadata.gz: 77ac60816689bd685f7ba0e6de2122bcd6e372bccee8b6bb1e1ef6bf660278fd3617ca7e5fe57debbe792db44baa030fc55d721681b535bb2e0de6ad435604ad
7
+ data.tar.gz: 0814a5099a9b4d7c246c9a9b3dabe05e8d234c6fc86f6022cfd3ba54a3f76df521dad46a1130ef4e422a1e1125c523543d2d22efca6d6067dba15f39589db889
data/README.md CHANGED
@@ -25,53 +25,77 @@ Keratin::AuthN.config.tap do |config|
25
25
 
26
26
  # The domain of your application
27
27
  config.audience = 'myapp.com'
28
+
29
+ # HTTP basic auth for using AuthN's private endpoints
30
+ config.username = 'secret'
31
+ config.password = 'secret'
28
32
  end
29
33
  ```
30
34
 
31
- Use `Keratin::AuthN.subject_from(params[:id_token])` to validate tokens and fetch an `account_id` during signup, login, and session verification.
35
+ ### Reading the Session
32
36
 
33
- Send users to `Keratin::AuthN.logout_url(return_to: some_path)` to log them out from the AuthN server.
37
+ Use `Keratin::AuthN.subject_from(params[:authn])` to fetch an `account_id` from the session if and
38
+ only if the session is valid.
34
39
 
35
- ### Example: Signup
40
+ ### Logging Out
41
+
42
+ Send users to `Keratin::AuthN.logout_url(return_to: some_path)` to log them out from the AuthN
43
+ server. If you use [keratin/authn-js](https://github.com/keratin/authn-js), you might prefer the
44
+ logout functionality there as it can also take care of deleting the cookie.
45
+
46
+ ### Modifying Accounts
47
+
48
+ * `Keratin.authn.lock(account_id)`: will lock an account, revoking all sessions (when they time out)
49
+ and disallowing any new logins. Intended for user moderation actions.
50
+ * `Keratin.authn.unlock(account_id)`: will unlock an account, restoring normal functionality.
51
+ * `Keratin.authn.archive(account_id)`: will wipe all personal information, including username and
52
+ password. Intended for user deletion routine.
53
+
54
+ ### Example: Sessions
55
+
56
+ You should store the token in a cookie (the [keratin/authn-js](https://github.com/keratin/authn-js)
57
+ integration can do this automatically) and continue using it to verify a logged-in session:
36
58
 
37
59
  ```ruby
38
- class UsersController
39
- def create
40
- @user = User.new(params.require(:user).permit(:name, :email))
41
- @user.account_id = Keratin::AuthN.subject_from(params[:user][:id_token])
60
+ class ApplicationController
61
+ private
42
62
 
43
- # ...
63
+ def logged_in?
64
+ !! current_account_id
65
+ end
66
+
67
+ def current_user
68
+ return @current_user if defined? @current_user
69
+ @current_user = User.find_by_account_id(current_account_id)
70
+ end
71
+
72
+ def current_account_id
73
+ Keratin::AuthN.subject_from(cookies[:authn])
44
74
  end
45
75
  end
46
76
  ```
47
77
 
48
- ### Example: Login
78
+ ### Example: Signup
49
79
 
50
80
  ```ruby
51
- class SessionsController
81
+ class UsersController
52
82
  def create
53
- @user = User.find_by_account_id(Keratin::AuthN.subject_from(cookies[:id_token]))
83
+ @user = User.new(params.require(:user).permit(:name, :email))
84
+ @user.account_id = current_account_id
54
85
 
55
86
  # ...
56
87
  end
57
88
  end
58
89
  ```
59
90
 
60
- ### Example: Sessions
61
-
62
- You should store the token in a cookie and continue using it to verify a logged-in session:
91
+ ### Example: Login
63
92
 
64
93
  ```ruby
65
- class ApplicationController
66
- private
67
-
68
- def logged_in?
69
- !! Keratin::AuthN.subject_from(cookies[:id_token])
70
- end
94
+ class SessionsController
95
+ def create
96
+ @user = current_user
71
97
 
72
- def current_user
73
- return @current_user if defined? @current_user
74
- @current_user = User.find_by_account_id(Keratin::AuthN.subject_from(cookies[:id_token])
98
+ # ...
75
99
  end
76
100
  end
77
101
  ```
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
- require "auth/rb"
4
+ require "keratin/authn"
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
data/lib/keratin/authn.rb CHANGED
@@ -6,52 +6,60 @@ require_relative 'authn/issuer'
6
6
  require 'lru_redux'
7
7
  require 'json/jwt'
8
8
 
9
- module Keratin::AuthN
10
- class Config
11
- # the domain (host) of the main application.
12
- # e.g. "audience.tech"
13
- attr_accessor :audience
14
-
15
- # the base url of the service handling authentication
16
- # e.g. "https://issuer.tech"
17
- attr_accessor :issuer
18
-
19
- # the path where we can fetch configuration from our issuer.
20
- #
21
- # default: "/configuration"
22
- attr_accessor :configuration_path
23
-
24
- # how long (in seconds) to keep keys in the keychain before refreshing.
25
- # default: 3600
26
- attr_accessor :keychain_ttl
9
+ module Keratin
10
+ def self.authn
11
+ @authn ||= AuthN::Issuer.new(AuthN.config.issuer,
12
+ username: AuthN.config.username,
13
+ password: AuthN.config.password
14
+ )
27
15
  end
28
16
 
29
- def self.config
30
- @config ||= Config.new.tap do |config|
31
- config.configuration_path = '/configuration'
32
- config.keychain_ttl = 3600
17
+ module AuthN
18
+ class Config
19
+ # the domain (host) of the main application.
20
+ # e.g. "audience.tech"
21
+ attr_accessor :audience
22
+
23
+ # the base url of the service handling authentication
24
+ # e.g. "https://issuer.tech"
25
+ attr_accessor :issuer
26
+
27
+ # how long (in seconds) to keep keys in the keychain before refreshing.
28
+ # default: 3600
29
+ attr_accessor :keychain_ttl
30
+
31
+ # the http basic auth username for accessing private endpoints of the authn issuer.
32
+ attr_accessor :username
33
+
34
+ # the http basic auth password for accessing private endpoints of the authn issuer.
35
+ attr_accessor :password
33
36
  end
34
- end
35
37
 
36
- def self.keychain
37
- @keychain ||= LruRedux::TTL::ThreadSafeCache.new(25, config.keychain_ttl)
38
- end
38
+ def self.config
39
+ @config ||= Config.new.tap do |config|
40
+ config.keychain_ttl = 3600
41
+ end
42
+ end
39
43
 
40
- class << self
41
- # safely fetches a subject from the id token after checking relevant claims and
42
- # verifying the signature.
43
- #
44
- # this may involve HTTP requests to fetch the issuer's configuration and JWKs.
45
- def subject_from(id_token)
46
- verifier = IDTokenVerifier.new(id_token, keychain)
47
- verifier.subject if verifier.verified?
44
+ def self.keychain
45
+ @keychain ||= LruRedux::TTL::ThreadSafeCache.new(25, config.keychain_ttl)
48
46
  end
49
47
 
50
- def logout_url(return_to: nil)
51
- query = {redirect_uri: return_to}.to_param if return_to
48
+ class << self
49
+ # safely fetches a subject from the id token after checking relevant claims and
50
+ # verifying the signature.
51
+ #
52
+ # this may involve HTTP requests to fetch the issuer's configuration and JWKs.
53
+ def subject_from(id_token)
54
+ verifier = IDTokenVerifier.new(id_token, keychain)
55
+ verifier.subject if verifier.verified?
56
+ end
52
57
 
53
- "#{config.issuer}/sessions/logout#{?? if query}#{query}"
58
+ def logout_url(return_to: nil)
59
+ query = {redirect_uri: return_to}.to_param if return_to
60
+
61
+ "#{config.issuer}/sessions/logout#{?? if query}#{query}"
62
+ end
54
63
  end
55
64
  end
56
-
57
65
  end
@@ -1,10 +1,18 @@
1
+ require 'keratin/client'
1
2
  require 'net/http'
2
3
 
3
4
  module Keratin::AuthN
4
- class Issuer
5
- def initialize(str)
6
- @uri = str
7
- @config_uri = @uri.chomp('/') + Keratin::AuthN.config.configuration_path
5
+ class Issuer < Keratin::Client
6
+ def lock(account_id)
7
+ patch(path: "/accounts/:account_id/lock").result
8
+ end
9
+
10
+ def unlock(account_id)
11
+ patch(path: "/accounts/:account_id/unlock").result
12
+ end
13
+
14
+ def archive(account_id)
15
+ delete(path: "/accounts/:account_id").result
8
16
  end
9
17
 
10
18
  def signing_key
@@ -12,16 +20,12 @@ module Keratin::AuthN
12
20
  end
13
21
 
14
22
  def configuration
15
- @configuration ||= JSON.parse(
16
- Net::HTTP.get(URI.parse(@config_uri))
17
- )
23
+ @configuration ||= get(path: '/configuration').data
18
24
  end
19
25
 
20
26
  def keys
21
27
  @keys ||= JSON::JWK::Set.new(
22
- JSON.parse(
23
- Net::HTTP.get(URI.parse(configuration['jwks_uri']))
24
- )
28
+ get(path: URI.parse(configuration['jwks_uri']).path).data
25
29
  )
26
30
  end
27
31
  end
@@ -26,7 +26,7 @@ module Keratin::AuthN
26
26
  # stubs the endpoints necessary to validate a signed JWT
27
27
  private def stub_auth_server(issuer: Keratin::AuthN.config.issuer, keypair: jws_keypair)
28
28
  Keratin::AuthN.keychain.clear
29
- stub_request(:get, "#{issuer}#{Keratin::AuthN.config.configuration_path}").to_return(
29
+ stub_request(:get, "#{issuer}/configuration").to_return(
30
30
  status: 200,
31
31
  body: {'jwks_uri' => "#{issuer}/jwks"}.to_json
32
32
  )
@@ -1,5 +1,5 @@
1
1
  module Keratin
2
2
  module AuthN
3
- VERSION = "0.1.3"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -0,0 +1,78 @@
1
+ module Keratin
2
+ class Error < StandardError; end
3
+
4
+ class ServiceResult
5
+ attr_reader :data
6
+ attr_reader :result
7
+
8
+ def initialize(data)
9
+ @data = data
10
+ @result = data['result']
11
+ end
12
+ end
13
+
14
+ class ClientError < Keratin::Error
15
+ attr_reader :errors
16
+
17
+ def initialize(errors)
18
+ @errors = errors.map{|e| [e['field'], e['message']] }
19
+ .group_by(&:first)
20
+ .map{|k, v| [k, v.map(&:last)] }
21
+ .to_h
22
+
23
+ super(@errors.inspect)
24
+ end
25
+ end
26
+
27
+ class ServiceError < Keratin::Error
28
+ end
29
+
30
+ class Client
31
+
32
+ attr_reader :base
33
+
34
+ def initialize(base_url, username: nil, password: nil)
35
+ @base = base_url.chomp('/')
36
+ @auth = [username, password] if username && password
37
+ end
38
+
39
+ private def get(**opts)
40
+ submit(Net::HTTP::Get, **opts)
41
+ end
42
+
43
+ private def patch(**opts)
44
+ submit(Net::HTTP::Patch, **opts)
45
+ end
46
+
47
+ private def delete(**opts)
48
+ submit(Net::HTTP::Delete, **opts)
49
+ end
50
+
51
+ private def submit(request_klass, path:)
52
+ uri = URI.parse("#{base}#{path}")
53
+
54
+ request = request_klass.new(uri)
55
+ request.basic_auth(*@auth) if @auth
56
+
57
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
58
+ http.open_timeout = 0.5
59
+ http.read_timeout = 2.0
60
+
61
+ case response = http.request(request)
62
+ when Net::HTTPSuccess
63
+ return ServiceResult.new(JSON.parse(response.body))
64
+ when Net::HTTPRedirection
65
+ return ServiceResult.new('result' => {
66
+ 'location' => response['Location']
67
+ })
68
+ when Net::HTTPClientError
69
+ raise ClientError.new(JSON.parse(response.body)['errors'])
70
+ when Net::HTTPServerError
71
+ raise ServiceError.new(response.body)
72
+ end
73
+ end
74
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
75
+ raise ServiceError.new(e.message)
76
+ end
77
+ end
78
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keratin-authn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lance Ivy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-28 00:00:00.000000000 Z
11
+ date: 2016-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json-jwt
@@ -132,6 +132,7 @@ files:
132
132
  - lib/keratin/authn/test/helpers.rb
133
133
  - lib/keratin/authn/testing.rb
134
134
  - lib/keratin/authn/version.rb
135
+ - lib/keratin/client.rb
135
136
  homepage:
136
137
  licenses:
137
138
  - LGPL-3.0