authify-api 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +97 -19
- data/authify-api.gemspec +3 -1
- data/db/schema.rb +63 -63
- data/lib/authify/api/controllers/organization.rb +1 -1
- data/lib/authify/api/helpers/jwt_encryption.rb +32 -3
- data/lib/authify/api/services/jwt_provider.rb +13 -0
- data/lib/authify/api/services/monitoring.rb +1 -10
- data/lib/authify/api/version.rb +1 -1
- metadata +32 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27ef09f0eac2be3b4726c08351dcffb65cfc8a4b
|
4
|
+
data.tar.gz: 19874f525600aec9655e219f8808c1346e336431
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f685845d5e55982ae4a47d191be11558fbd950c2b75a917bd1f280e8f2e6496e409bebc4b59ae2dee8e39db37bf837f38073782479a93f3a76cf12ddfa286e7c
|
7
|
+
data.tar.gz: dbed51e9635435d3a4c7c05467a1de6865bcf6118c5e6e69dac0ab532177466d536bfebc62be48e1a00e45a13ac26e5f66fb5c9d623f2208a054a52fac7d0c7c
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Authify::API
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/authify-api.svg)](https://badge.fury.io/rb/authify-api)
|
4
|
+
[![Build Status](https://travis-ci.org/knuedge/authify-api.svg?branch=master)](https://travis-ci.org/knuedge/authify-api)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/github/knuedge/authify-api/badge.svg?branch=master)](https://coveralls.io/github/knuedge/authify-api?branch=master)
|
6
|
+
|
3
7
|
## Introduction
|
4
8
|
|
5
9
|
Authify is a web service built from the ground up to simplify authentication and provide it securely to a collection of related web sites.
|
@@ -17,24 +21,87 @@ The Authify API service consists of a database for storing:
|
|
17
21
|
* Groups (and membership)
|
18
22
|
* Trusted authify delegates (other services with unlimited capabilities, including impersonating users)
|
19
23
|
|
20
|
-
Nearly all API endpoints available via Authify implement the [{json:api}](http://jsonapi.org/) 1.0 specification
|
24
|
+
Nearly all API endpoints available via Authify implement the [{json:api}](http://jsonapi.org/) 1.0 specification, though there are a few exceptions.
|
25
|
+
|
26
|
+
### Non-standard API Endpoints
|
27
|
+
|
28
|
+
**`GET /jwt/key`**
|
29
|
+
_Returns Content Type: `application/json`._
|
30
|
+
|
31
|
+
This endpoint returns a JSON Object with the key `data` whose value is a PEM-encoded ECDSA public key, which should be used to verify the signature made by the Authify service.
|
32
|
+
|
33
|
+
**`GET /jwt/meta`**
|
34
|
+
_Returns Content Type: `application/json`._
|
35
|
+
|
36
|
+
This endpoint returns a JSON Object with the keys `algorithm`, `issuer`, and `expiration` that describe the kind of JWTs produced by this service.
|
37
|
+
|
38
|
+
**`POST /jwt/token`**
|
39
|
+
_Returns (and only accepts) Content Type: `application/json`._
|
40
|
+
|
41
|
+
This endpoint is used to obtain a [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token). This endpoint expects a JSON Object with either the keys `access_key` and `secret_key` _OR_ `email` and `password`. There is no firm requirement to use either pair for any particular purpose, but for scenarios where the credentials may be stored, the `access_key` and `secret_key` can easily be revoked if necessary.
|
42
|
+
|
43
|
+
Upon successful authentication, the endpoint provides a JSON Object with the key `jwt` and a signed JWT. There should be nothing highly sensitive embedded in the JWT. The JWT defaults to expiring every 15 minutes.
|
44
|
+
|
45
|
+
This endpoint also allows optionally specifying a key called `inject` with a JSON object as a value. This JSON object will then be injected into a top-level `custom` key in the returned JWT _as is_.
|
46
|
+
|
47
|
+
**`GET/POST /jwt/verify`**
|
48
|
+
_Returns (and only accepts) Content Type: `application/json`_
|
49
|
+
|
50
|
+
This endpoint is useful for debugging or for low-volume, simple clients. Pass either a `GET` parameter of `token` or `POST` a JSON object with the key `token`. In either case, the value is a JWT that can be validated and have its details returned as simple JSON data.
|
51
|
+
|
52
|
+
For valid JWTs, this endpoint will return a JSON object with the keys `valid`, `payload`, `type`, and `algorithm`. The `valid` field is a boolean that describes whether or not the JWT is valid for use with this instance of Authify. The `payload` field is the full JWT payload, with all its claims listed as keys in a JSON object. The `type` key should always return `JWT` but is reserved for future use. Finally, the `algorithm` key describes the JWA algorithm used to sign the key. See the [configuration section](#configuration) for details on the algorithm.
|
53
|
+
|
54
|
+
For invalid or expired JWTs, this endpoint will still return `200 OK`, so don't rely on that to determine if the JWT is valid. It will, however, return different data. In this case, the endpoint will respond with a JSON object with the keys `valid`, `errors`, and `reason`. For invalid JWTs, the `valid` boolean will be `false`. The `errors` key will be a list of errors encountered while processing the JWT. The `reason` key provides a simple and generic explanation of the first encountered failure.
|
55
|
+
|
56
|
+
**`POST /registration/signup`**
|
57
|
+
_Returns (and only accepts) Content Type: `application/json`._
|
58
|
+
|
59
|
+
This endpoint is used to signup for an account with Authify. This endpoint expects a JSON Object, requiring the keys `email` and `password`, with `name` and `via` being optional. If `via` is provided, then it must be a JSON Object with the keys `provider` and `uid`, otherwise it will be ignored. The `via` key is used to add an alternate identity (meaning they logged-in through an integration, like Github), and is only trusted from trusted delegates (meaning it will be ignored for anonymous calls to this endpoint).
|
60
|
+
|
61
|
+
This endpoint returns a JSON Object with the keys `id`, `email`, and `verified`, on success. If the user is registered by a trusted delegate *and* `via` options were provided, the users is implicitly trusted and a `jwt` key will also be provided for authentication. Otherwise, users will need to proceed to `/registration/verify` with the token they receive by email to verify their identity.
|
62
|
+
|
63
|
+
This endpoint allows customization of the emails sent for users requiring verification. For information on how this works, see the [Templating](#templating) section. The following template expressions are available: `token` and `valid_until`.
|
21
64
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
65
|
+
**`POST /registration/verify`**
|
66
|
+
_Returns (and only accepts) Content Type: `application/json`._
|
67
|
+
|
68
|
+
This endpoint is used to verify a registered user's email address. Currently, the data used to verify users is a token provided via email.
|
69
|
+
|
70
|
+
This endpoint expects a JSON Object, requiring the keys `email`, `password`, and `token`. This endpoint returns a JSON Object with the keys `id`, `email`, `verified`, and `jwt` on success.
|
71
|
+
|
72
|
+
**`POST /registration/forgot_password`**
|
73
|
+
_Returns (and only accepts) Content Type: `application/json`._
|
74
|
+
|
75
|
+
This endpoint serves two related purposes: it is used to trigger resetting a forgotten (or non-existent) password and it is used to actually set the value of a user's password. The difference in which operation is performed is based on the POST data.
|
76
|
+
|
77
|
+
When provided a JSON Object with only the key `email`, the endpoint sends the user an email with a verification token, returning an empty JSON Object as a result. When provided a JSON Object with the keys `email`, `password`, and `token`, the endpoint verifies that the token matches, then sets the user's password, returning a JSON Object with the keys `id`, `email`, `verified`, and `jwt` on success.
|
78
|
+
|
79
|
+
This endpoint allows customization of the emails sent for users requiring verification. For information on how this works, see the [Templating](#templating) section. The following template expressions are available: `token` and `valid_until`.
|
80
|
+
|
81
|
+
### {json:api} API Endpoints
|
28
82
|
|
29
83
|
All other endpoints adhere to the {json:api} specification and can be found at the following base paths:
|
30
84
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
85
|
+
**`/apikeys`**
|
86
|
+
User API keys. Index is restricted. Should only really be useful for users manipulating their own keys.
|
87
|
+
|
88
|
+
**`/groups`**
|
89
|
+
Groups. Index is restricted. Most interactions with groups should be scoped via organizations.
|
90
|
+
|
91
|
+
**`/identities`**
|
92
|
+
Alternate User Identities. These are other services that the user can login via (web UI only).
|
36
93
|
|
37
|
-
|
94
|
+
**`/organizations`**
|
95
|
+
Organizations. These are high-level groupings of users and groups. Non-administrators should only be able to see limited amounts of information about organizations.
|
96
|
+
|
97
|
+
**`/users`**
|
98
|
+
Users controller.
|
99
|
+
|
100
|
+
### Trusted Delegates
|
101
|
+
|
102
|
+
In addition to expiring JWTs provided via `/jwt/token` for normal user interactions, Trusted Delegates can perform any action by providing the `X-Authify-Access`, `X-Authify-Secret`, and the `X-Authify-On-Behalf-Of` headers. The `Access` and `Secret` headers are used to authenticate the remote application, and the `On-Behalf-Of` is used to impersonate the user (determined through a process on the remote, trusted delegate's end to establish the user's identity).
|
103
|
+
|
104
|
+
Note that while these sound similar to User API keys, these Trusted Delegate credentials are longer and can not be interchanged with User API Keys. These values do not expire and are not easily created or removed. For this reason, they should be used **very** sparingly. They can only be created, listed, or removed via a set of `rake` commands run server-side. These are:
|
38
105
|
|
39
106
|
* `rake delegate:add[<name>]` - where `<name>` is the unique name of the trusted delegate. For example, `rake delegate:add[foo]` adds a remote delegate named `foo`. This command will output a key / value set providing the access\_key and secret\_key. The secret\_key is stored as a one-way hash in the DB, so it can never be retrieved again.
|
40
107
|
* `rake delegate:list` - lists the names of all trusted delegates along with their access keys.
|
@@ -60,12 +127,23 @@ Or install it yourself as:
|
|
60
127
|
|
61
128
|
The Authify API services supports the following configuration settings, managed via environment variables of the same name:
|
62
129
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
130
|
+
**`AUTHIFY_DB_URL`**
|
131
|
+
The URL used by [ActiveRecord](http://guides.rubyonrails.org/configuring.html#configuring-a-database) to connect to the database. Currently supports `mysql2://` or `sqlite3://` URLs, though any driver supported by ActiveRecord should work if the required gems are installed. Defaults to `mysql2://root@localhost:3306/authifydb`.
|
132
|
+
|
133
|
+
**`AUTHIFY_PUBKEY_PATH`**
|
134
|
+
The path on the filesystem to the PEM-encoded, public ECDSA key. Defaults to `~/.authify/ssl/public.pem`.
|
135
|
+
|
136
|
+
**`AUTHIFY_PRIVKEY_PATH`**
|
137
|
+
The path on the filesystem to the PEM-encoded, private ECDSA key. Currently, Authify only supports an [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) keys. Options include using a `secp521r1` curve and the [SHA-512](https://en.wikipedia.org/wiki/SHA-2) hashing algorithm (called `ES512`), a `secp384r1` curve and the SHA-384 hashing algorithm (called `ES384`), or a `prime256v1` curve and the SHA-256 hashing algorithm (called `ES256`). See `AUTHIFY_JWT_ALGORITHM` below for information on how to configure Authify's algorithm to match the public and private keys you provide. The keys you specify **must** match the ECDSA algortihm and curve used to create them.
|
138
|
+
|
139
|
+
**`AUTHIFY_JWT_ISSUER`**
|
140
|
+
The name of the issuer ([iss field](https://en.wikipedia.org/wiki/JSON_Web_Token#Standard_fields)) used when creating the JWT. This **must** match on any service that verifies the JWT (meaning any service relying on Authify for authentication), and it **must** be the same for all services that integrate with Authify.
|
141
|
+
|
142
|
+
**`AUTHIFY_JWT_ALGORITHM`**
|
143
|
+
The name of the [JWA](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40) algorithm to use when loading keys and creating or verifying JWT signatures. Valid values are `ES256`, `ES384`, or `ES512`. Defaults to `ES512`. This **must** match the curve and algorithm used to produce the public and private keys found at `AUTHIFY_PUBKEY_PATH` and `AUTHIFY_PRIVKEY_PATH`, respectively. Note that the curves `prime256v1` (also called NIST P-256) used by `ES256` and `secp384r1` (also called NIST P-384) used by `ES384`, while offering a wider range of compatible SSL libraries, are described as unsafe on [SafeCurves](https://safecurves.cr.yp.to/) for several reasons described there.
|
144
|
+
|
145
|
+
**`AUTHIFY_JWT_EXPIRATION`**
|
146
|
+
How long should a JWT be valid (in minutes). Defaults to 15. Too small of a value will mean a lot more requests to the API; too high increases the possibility of viable keys being captured.
|
69
147
|
|
70
148
|
## Usage and Authentication Workflow
|
71
149
|
|
data/authify-api.gemspec
CHANGED
@@ -45,6 +45,8 @@ Gem::Specification.new do |spec|
|
|
45
45
|
spec.add_development_dependency 'rubocop', '~> 0.35'
|
46
46
|
spec.add_development_dependency 'yard', '~> 0.8'
|
47
47
|
spec.add_development_dependency 'travis', '~> 1.8'
|
48
|
-
spec.add_development_dependency 'simplecov', '~> 0.
|
48
|
+
spec.add_development_dependency 'simplecov', '~> 0.14'
|
49
|
+
spec.add_development_dependency 'coveralls', '~> 0.8'
|
50
|
+
spec.add_development_dependency 'byebug', '~> 9'
|
49
51
|
spec.add_development_dependency 'rack-test', '~> 0.6'
|
50
52
|
end
|
data/db/schema.rb
CHANGED
@@ -10,91 +10,91 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 20170609181046) do
|
14
14
|
|
15
|
-
create_table "apikeys", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
16
|
-
t.integer
|
17
|
-
t.string
|
18
|
-
t.text
|
19
|
-
t.datetime "created_at",
|
20
|
-
t.datetime "updated_at",
|
21
|
-
t.index ["access_key"], name: "index_apikeys_on_access_key"
|
22
|
-
t.index ["user_id"], name: "index_apikeys_on_user_id"
|
15
|
+
create_table "apikeys", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
16
|
+
t.integer "user_id"
|
17
|
+
t.string "access_key"
|
18
|
+
t.text "secret_key_digest"
|
19
|
+
t.datetime "created_at", null: false
|
20
|
+
t.datetime "updated_at", null: false
|
21
|
+
t.index ["access_key"], name: "index_apikeys_on_access_key"
|
22
|
+
t.index ["user_id"], name: "index_apikeys_on_user_id"
|
23
23
|
end
|
24
24
|
|
25
|
-
create_table "groups", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
26
|
-
t.integer
|
27
|
-
t.string
|
28
|
-
t.text
|
29
|
-
t.datetime "created_at",
|
30
|
-
t.datetime "updated_at",
|
31
|
-
t.index ["name"], name: "index_groups_on_name"
|
32
|
-
t.index ["organization_id"], name: "index_groups_on_organization_id"
|
25
|
+
create_table "groups", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
26
|
+
t.integer "organization_id"
|
27
|
+
t.string "name"
|
28
|
+
t.text "description"
|
29
|
+
t.datetime "created_at", null: false
|
30
|
+
t.datetime "updated_at", null: false
|
31
|
+
t.index ["name"], name: "index_groups_on_name"
|
32
|
+
t.index ["organization_id"], name: "index_groups_on_organization_id"
|
33
33
|
end
|
34
34
|
|
35
35
|
create_table "groups_users", id: false, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
36
36
|
t.integer "group_id"
|
37
37
|
t.integer "user_id"
|
38
|
-
t.index ["group_id"], name: "index_groups_users_on_group_id"
|
39
|
-
t.index ["user_id"], name: "index_groups_users_on_user_id"
|
38
|
+
t.index ["group_id"], name: "index_groups_users_on_group_id"
|
39
|
+
t.index ["user_id"], name: "index_groups_users_on_user_id"
|
40
40
|
end
|
41
41
|
|
42
|
-
create_table "identities", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
43
|
-
t.integer
|
44
|
-
t.string
|
45
|
-
t.string
|
42
|
+
create_table "identities", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
43
|
+
t.integer "user_id"
|
44
|
+
t.string "provider"
|
45
|
+
t.string "uid"
|
46
46
|
t.datetime "created_at", null: false
|
47
47
|
t.datetime "updated_at", null: false
|
48
|
-
t.index ["provider"], name: "index_identities_on_provider"
|
49
|
-
t.index ["uid"], name: "index_identities_on_uid"
|
50
|
-
t.index ["user_id"], name: "index_identities_on_user_id"
|
48
|
+
t.index ["provider"], name: "index_identities_on_provider"
|
49
|
+
t.index ["uid"], name: "index_identities_on_uid"
|
50
|
+
t.index ["user_id"], name: "index_identities_on_user_id"
|
51
51
|
end
|
52
52
|
|
53
|
-
create_table "organization_memberships", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
53
|
+
create_table "organization_memberships", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
54
54
|
t.integer "organization_id"
|
55
55
|
t.integer "user_id"
|
56
|
-
t.boolean "admin",
|
57
|
-
t.index ["admin"], name: "index_organization_memberships_on_admin"
|
58
|
-
t.index ["organization_id"], name: "index_organization_memberships_on_organization_id"
|
59
|
-
t.index ["user_id"], name: "index_organization_memberships_on_user_id"
|
56
|
+
t.boolean "admin", default: false
|
57
|
+
t.index ["admin"], name: "index_organization_memberships_on_admin"
|
58
|
+
t.index ["organization_id"], name: "index_organization_memberships_on_organization_id"
|
59
|
+
t.index ["user_id"], name: "index_organization_memberships_on_user_id"
|
60
60
|
end
|
61
61
|
|
62
|
-
create_table "organizations", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
63
|
-
t.string
|
64
|
-
t.string
|
65
|
-
t.string
|
66
|
-
t.string
|
67
|
-
t.text
|
68
|
-
t.string
|
69
|
-
t.string
|
70
|
-
t.datetime "created_at",
|
71
|
-
t.datetime "updated_at",
|
72
|
-
t.index ["name"], name: "index_organizations_on_name"
|
62
|
+
create_table "organizations", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
63
|
+
t.string "name"
|
64
|
+
t.string "public_email"
|
65
|
+
t.string "gravatar_email"
|
66
|
+
t.string "billing_email"
|
67
|
+
t.text "description"
|
68
|
+
t.string "url"
|
69
|
+
t.string "location"
|
70
|
+
t.datetime "created_at", null: false
|
71
|
+
t.datetime "updated_at", null: false
|
72
|
+
t.index ["name"], name: "index_organizations_on_name"
|
73
73
|
end
|
74
74
|
|
75
|
-
create_table "trusted_delegates", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
76
|
-
t.string
|
77
|
-
t.string
|
78
|
-
t.text
|
79
|
-
t.text
|
80
|
-
t.datetime "created_at",
|
81
|
-
t.datetime "updated_at",
|
82
|
-
t.index ["access_key"], name: "index_trusted_delegates_on_access_key"
|
83
|
-
t.index ["name"], name: "index_trusted_delegates_on_name"
|
75
|
+
create_table "trusted_delegates", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
76
|
+
t.string "name"
|
77
|
+
t.string "access_key"
|
78
|
+
t.text "secret_key_digest"
|
79
|
+
t.text "description"
|
80
|
+
t.datetime "created_at", null: false
|
81
|
+
t.datetime "updated_at", null: false
|
82
|
+
t.index ["access_key"], name: "index_trusted_delegates_on_access_key"
|
83
|
+
t.index ["name"], name: "index_trusted_delegates_on_name"
|
84
84
|
end
|
85
85
|
|
86
|
-
create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
87
|
-
t.string
|
88
|
-
t.text
|
89
|
-
t.string
|
90
|
-
t.datetime "created_at",
|
91
|
-
t.datetime "updated_at",
|
92
|
-
t.boolean
|
93
|
-
t.boolean
|
94
|
-
t.string
|
95
|
-
t.index ["admin"], name: "index_users_on_admin"
|
96
|
-
t.index ["email"], name: "index_users_on_email"
|
97
|
-
t.index ["verified"], name: "index_users_on_verified"
|
86
|
+
create_table "users", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
87
|
+
t.string "email"
|
88
|
+
t.text "password_digest"
|
89
|
+
t.string "full_name"
|
90
|
+
t.datetime "created_at", null: false
|
91
|
+
t.datetime "updated_at", null: false
|
92
|
+
t.boolean "admin", default: false, null: false
|
93
|
+
t.boolean "verified"
|
94
|
+
t.string "verification_token"
|
95
|
+
t.index ["admin"], name: "index_users_on_admin"
|
96
|
+
t.index ["email"], name: "index_users_on_email"
|
97
|
+
t.index ["verified"], name: "index_users_on_verified"
|
98
98
|
end
|
99
99
|
|
100
100
|
end
|
@@ -9,7 +9,7 @@ module Authify
|
|
9
9
|
|
10
10
|
def role
|
11
11
|
Array(super).tap do |a|
|
12
|
-
a << :owner if resource && current_user.admin_for?(resource)
|
12
|
+
a << :owner if resource && current_user && current_user.admin_for?(resource)
|
13
13
|
a << :member if resource && resource.users.include?(current_user)
|
14
14
|
end.uniq
|
15
15
|
end
|
@@ -5,12 +5,13 @@ module Authify
|
|
5
5
|
module JWTEncryption
|
6
6
|
include Core::Helpers::JWTSSL
|
7
7
|
|
8
|
-
def jwt_token(user: nil, custom_data: {})
|
8
|
+
def jwt_token(user: nil, custom_data: {}, meta: nil)
|
9
9
|
user ||= current_user
|
10
|
-
JWT.encode jwt_payload(user, custom_data), private_key, CONFIG[:jwt][:algorithm]
|
10
|
+
JWT.encode jwt_payload(user, custom_data, meta), private_key, CONFIG[:jwt][:algorithm]
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
# rubocop:disable Metrics/AbcSize
|
14
|
+
def jwt_payload(user, custom_data, metadata = nil)
|
14
15
|
data = {
|
15
16
|
exp: Time.now.to_i + 60 * CONFIG[:jwt][:expiration].to_i,
|
16
17
|
iat: Time.now.to_i,
|
@@ -25,9 +26,37 @@ module Authify
|
|
25
26
|
}
|
26
27
|
}
|
27
28
|
data[:custom] = custom_data if custom_data && !custom_data.empty?
|
29
|
+
data[:meta] = metadata if metadata && metadata.is_a?(Hash) && !metadata.empty?
|
28
30
|
data
|
29
31
|
end
|
30
32
|
|
33
|
+
def jwt_options
|
34
|
+
{
|
35
|
+
algorithm: CONFIG[:jwt][:algorithm],
|
36
|
+
verify_iss: true,
|
37
|
+
verify_iat: true,
|
38
|
+
iss: CONFIG[:jwt][:issuer]
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def process_token(token)
|
43
|
+
results = {}
|
44
|
+
|
45
|
+
begin
|
46
|
+
decoded = JWT.decode(token, public_key, true, jwt_options)
|
47
|
+
|
48
|
+
results[:valid] = true
|
49
|
+
results[:payload] = decoded[0]
|
50
|
+
results[:type] = decoded[1]['typ']
|
51
|
+
results[:algorithm] = decoded[1]['alg']
|
52
|
+
rescue JWT::DecodeError => e
|
53
|
+
results[:valid] = false
|
54
|
+
results[:errors] = Array[e]
|
55
|
+
results[:reason] = 'Corrupt or invalid JWT'
|
56
|
+
end
|
57
|
+
results
|
58
|
+
end
|
59
|
+
|
31
60
|
def simple_orgs_by_user(user)
|
32
61
|
user.organizations.map do |o|
|
33
62
|
{
|
@@ -70,6 +70,7 @@ module Authify
|
|
70
70
|
if found_user
|
71
71
|
update_current_user found_user
|
72
72
|
Metrics.instance.increment('jwt.tokens.provided')
|
73
|
+
|
73
74
|
{ jwt: jwt_token(custom_data: custom_data) }.to_json
|
74
75
|
else
|
75
76
|
halt 401
|
@@ -103,6 +104,18 @@ module Authify
|
|
103
104
|
options '/key' do
|
104
105
|
halt 200
|
105
106
|
end
|
107
|
+
|
108
|
+
get '/verify' do
|
109
|
+
process_token(@params[:token]).to_json
|
110
|
+
end
|
111
|
+
|
112
|
+
post '/verify' do
|
113
|
+
process_token(@parsed_body[:token]).to_json
|
114
|
+
end
|
115
|
+
|
116
|
+
options '/verify' do
|
117
|
+
halt 200
|
118
|
+
end
|
106
119
|
end
|
107
120
|
end
|
108
121
|
end
|
@@ -9,20 +9,11 @@ module Authify
|
|
9
9
|
|
10
10
|
before do
|
11
11
|
content_type 'application/json'
|
12
|
-
|
13
|
-
begin
|
14
|
-
unless request.get? || request.options?
|
15
|
-
request.body.rewind
|
16
|
-
@parsed_body = JSON.parse(request.body.read, symbolize_names: true)
|
17
|
-
end
|
18
|
-
rescue => e
|
19
|
-
halt(400, { error: "Request must be valid JSON: #{e.message}" }.to_json)
|
20
|
-
end
|
21
12
|
end
|
22
13
|
|
23
14
|
after do
|
24
15
|
headers 'Access-Control-Allow-Origin' => '*',
|
25
|
-
'Access-Control-Allow-Methods' => %w[OPTIONS GET
|
16
|
+
'Access-Control-Allow-Methods' => %w[OPTIONS GET],
|
26
17
|
'Access-Control-Allow-Headers' => %w[
|
27
18
|
Origin
|
28
19
|
Accept
|
data/lib/authify/api/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authify-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Gnagy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: authify-core
|
@@ -308,14 +308,42 @@ dependencies:
|
|
308
308
|
requirements:
|
309
309
|
- - "~>"
|
310
310
|
- !ruby/object:Gem::Version
|
311
|
-
version: '0.
|
311
|
+
version: '0.14'
|
312
312
|
type: :development
|
313
313
|
prerelease: false
|
314
314
|
version_requirements: !ruby/object:Gem::Requirement
|
315
315
|
requirements:
|
316
316
|
- - "~>"
|
317
317
|
- !ruby/object:Gem::Version
|
318
|
-
version: '0.
|
318
|
+
version: '0.14'
|
319
|
+
- !ruby/object:Gem::Dependency
|
320
|
+
name: coveralls
|
321
|
+
requirement: !ruby/object:Gem::Requirement
|
322
|
+
requirements:
|
323
|
+
- - "~>"
|
324
|
+
- !ruby/object:Gem::Version
|
325
|
+
version: '0.8'
|
326
|
+
type: :development
|
327
|
+
prerelease: false
|
328
|
+
version_requirements: !ruby/object:Gem::Requirement
|
329
|
+
requirements:
|
330
|
+
- - "~>"
|
331
|
+
- !ruby/object:Gem::Version
|
332
|
+
version: '0.8'
|
333
|
+
- !ruby/object:Gem::Dependency
|
334
|
+
name: byebug
|
335
|
+
requirement: !ruby/object:Gem::Requirement
|
336
|
+
requirements:
|
337
|
+
- - "~>"
|
338
|
+
- !ruby/object:Gem::Version
|
339
|
+
version: '9'
|
340
|
+
type: :development
|
341
|
+
prerelease: false
|
342
|
+
version_requirements: !ruby/object:Gem::Requirement
|
343
|
+
requirements:
|
344
|
+
- - "~>"
|
345
|
+
- !ruby/object:Gem::Version
|
346
|
+
version: '9'
|
319
347
|
- !ruby/object:Gem::Dependency
|
320
348
|
name: rack-test
|
321
349
|
requirement: !ruby/object:Gem::Requirement
|