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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 14d6a59892c799848cfafc2560f6c8a563d47931
4
- data.tar.gz: ac147175656a4ba68993aa277a08a54636dd5fd6
3
+ metadata.gz: 27ef09f0eac2be3b4726c08351dcffb65cfc8a4b
4
+ data.tar.gz: 19874f525600aec9655e219f8808c1346e336431
5
5
  SHA512:
6
- metadata.gz: d70a5903b23f8cd25965bba23345a61d1a076eae15187ff79882131d0b7c1a408ffb08708d90e6ca84a60762327b6354ed59647b4032d7592f63ca18a48176fc
7
- data.tar.gz: 9eb2b416d3ce781d6f1abd6891790ab359bfda9aff40adee30ebc72b4f775a551116cc121dcd21f4b99714b80ef5a91862042bd4d286f5656015fcceb495c56b
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. The exceptions are:
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
- * `GET /jwt/key` - Returns Content Type: `application/json`. 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.
23
- * `GET /jwt/meta` - Returns Content Type: `application/json`. This endpoint returns a JSON Object with the keys `algorithm`, `issuer`, and `expiration` that describe the kind of JWTs produced by this service.
24
- * `POST /jwt/token` - Returns (and only accepts) Content Type: `application/json`. 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` may be used since those can easily be revoked if necessary. 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. 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_.
25
- * `POST /registration/signup` - Returns (and only accepts) Content Type: `application/json`. 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). 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. 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`.
26
- * `POST /registration/verify` - Returns (and only accepts) Content Type: `application/json`. This endpoint is used to verify a registered user's email address. 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.
27
- * `POST /registration/forgot_password` - Returns (and only accepts) Content Type: `application/json`. 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. 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. 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`.
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
- * `/apikeys` - User API keys. Index is restricted. Should only really be useful for users manipulating their own keys.
32
- * `/groups` - Groups. Index is restricted. Most interactions with groups should be scoped via organizations.
33
- * `/identities` - Alternate User Identities. These are other services that the user can login via (web UI only).
34
- * `/organizations` - Organizations. These are high-level groupings of users and groups. Non-administrators should only be able to see limited amounts of information about organizations.
35
- * `/users` - Users controller.
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
- 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 (usually determined through a process on the remote end to establish the user's identity). Note that while these sound similar to User API keys, these Trusted Delegate credentials are longer and can not be interchanged. 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:
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
- * `AUTHIFY_DB_URL` - 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`.
64
- * `AUTHIFY_PUBKEY_PATH` - The path on the filesystem to the PEM-encoded, public ECDSA key. Defaults to `~/.authify/ssl/public.pem`.
65
- * `AUTHIFY_PRIVKEY_PATH` - 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.
66
- * `AUTHIFY_JWT_ISSUER` - 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.
67
- * `AUTHIFY_JWT_ALGORITHM` - 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.
68
- * `AUTHIFY_JWT_EXPIRATION` - 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.
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.13'
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: 20170328151033) do
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 "user_id"
17
- t.string "access_key"
18
- t.text "secret_key_digest", limit: 65535
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", using: :btree
22
- t.index ["user_id"], name: "index_apikeys_on_user_id", using: :btree
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 "organization_id"
27
- t.string "name"
28
- t.text "description", limit: 65535
29
- t.datetime "created_at", null: false
30
- t.datetime "updated_at", null: false
31
- t.index ["name"], name: "index_groups_on_name", using: :btree
32
- t.index ["organization_id"], name: "index_groups_on_organization_id", using: :btree
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", using: :btree
39
- t.index ["user_id"], name: "index_groups_users_on_user_id", using: :btree
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 "user_id"
44
- t.string "provider"
45
- t.string "uid"
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", using: :btree
49
- t.index ["uid"], name: "index_identities_on_uid", using: :btree
50
- t.index ["user_id"], name: "index_identities_on_user_id", using: :btree
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", default: false
57
- t.index ["admin"], name: "index_organization_memberships_on_admin", using: :btree
58
- t.index ["organization_id"], name: "index_organization_memberships_on_organization_id", using: :btree
59
- t.index ["user_id"], name: "index_organization_memberships_on_user_id", using: :btree
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 "name"
64
- t.string "public_email"
65
- t.string "gravatar_email"
66
- t.string "billing_email"
67
- t.text "description", limit: 65535
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", using: :btree
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 "name"
77
- t.string "access_key"
78
- t.text "secret_key_digest", limit: 65535
79
- t.text "description", limit: 65535
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", using: :btree
83
- t.index ["name"], name: "index_trusted_delegates_on_name", using: :btree
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 "email"
88
- t.text "password_digest", limit: 65535
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", using: :btree
96
- t.index ["email"], name: "index_users_on_email", using: :btree
97
- t.index ["verified"], name: "index_users_on_verified", using: :btree
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
- def jwt_payload(user, custom_data)
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 POST],
16
+ 'Access-Control-Allow-Methods' => %w[OPTIONS GET],
26
17
  'Access-Control-Allow-Headers' => %w[
27
18
  Origin
28
19
  Accept
@@ -3,7 +3,7 @@ module Authify
3
3
  VERSION = [
4
4
  0, # Major
5
5
  4, # Minor
6
- 0 # Patch
6
+ 1 # Patch
7
7
  ].join('.')
8
8
  end
9
9
  end
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.0
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-08 00:00:00.000000000 Z
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.13'
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.13'
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