README.md in rodauth-oauth-0.0.3 vs README.md in rodauth-oauth-0.0.4
- old
+ new
@@ -13,10 +13,12 @@
* [Authorization grant flow](https://tools.ietf.org/html/rfc6749#section-1.3);
* [Access Token generation](https://tools.ietf.org/html/rfc6749#section-1.4);
* [Access Token refresh](https://tools.ietf.org/html/rfc6749#section-1.5);
* [Implicit grant (off by default)[https://tools.ietf.org/html/rfc6749#section-4.2];
* [Token revocation](https://tools.ietf.org/html/rfc7009);
+* [Token introspection](https://tools.ietf.org/html/rfc7662);
+* [Authorization Server Metadata](https://tools.ietf.org/html/rfc8414);
* [PKCE](https://tools.ietf.org/html/rfc7636);
* Access Type (Token refresh online and offline);
* [MAC Authentication Scheme](https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-02);
* [JWT Acess Tokens](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-07);
* OAuth application and token management dashboards;
@@ -144,14 +146,13 @@
Token revocation can be done both by the idenntity owner or the application owner, and can therefore be done either online (browser-based form) or server-to-server. Here's an example using server-to-server:
```ruby
require "httpx"
-httpx = HTTPX.plugin(:authorization)
-response = httpx.with(headers: { "X-your-auth-scheme" => ENV["SERVER_KEY"] })
+httpx = HTTPX.plugin(:basic_authorization)
+response = httpx.basic_authentication(ENV["CLIENT_ID"], ENV["CLIENT_SECRET"])
.post("https://auth_server/oauth-revoke",json: {
- client_id: ENV["OAUTH_CLIENT_ID"],
token_type_hint: "access_token", # can also be "refresh:tokn"
token: "2r89hfef4j9f90d2j2390jf390g"
})
response.raise_for_status
payload = JSON.parse(response.to_s)
@@ -162,10 +163,59 @@
```
> curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","token_type_hint":"access_token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/oauth-revoke
```
+#### Token introspection
+
+Token revocation can be used to determine the state of a token (whether active, what's the scope...) . Here's an example using server-to-server:
+
+```ruby
+require "httpx"
+httpx = HTTPX.plugin(:basic_authorization)
+response = httpx.basic_authentication(ENV["CLIENT_ID"], ENV["CLIENT_SECRET"])
+ .post("https://auth_server/oauth-introspect",json: {
+ token_type_hint: "access_token", # can also be "refresh:tokn"
+ token: "2r89hfef4j9f90d2j2390jf390g"
+ })
+response.raise_for_status
+payload = JSON.parse(response.to_s)
+puts payload #=> {"active" => true, "scope" => "read write" ....
+```
+
+##### cURL
+
+```
+> curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","token_type_hint":"access_token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/oauth-revoke
+```
+
+### Authorization Server Metadata
+
+The Authorization Server Metadata endpoint can be used by clients to obtain the information needed to interact with an
+ OAuth 2.0 authorization server, i.e. know which endpoint is used to authorize clients.
+
+Because this endpoint **must be https://AUTHSERVER/.well-known/oauth-authorization-server**, you'll have to define it at the root-level of your app:
+
+```ruby
+plugin :rodauth do
+ # enable it in the plugin
+ enable :login, :oauth
+ oauth_application_default_scope %w[profile.read]
+ oauth_application_scopes %w[profile.read profile.write]
+end
+
+# then, inside roda
+
+route do |r|
+ r.rodauth
+ # server metadata endpoint
+ rodauth.oauth_server_metadata
+
+ # now, your oauth and app code...
+
+```
+
### Database migrations
You have to generate database tables for Oauth applications, grants and tokens. In order for you to hit the ground running, [here's a set of migrations (using `sequel`) to generate the needed tables](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/tree/master/test/migrate) (omit the first 2 if you already have account tables).
You can change column names or even use existing tables, however, be aware that you'll have to define new column accessors at the `rodauth` plugin declaration level. Let's say, for instance, you'd like to change the `oauth_grants` table name to `access_grants`, and it's `code` column to `authorization_code`; then, you'd have to do the following:
@@ -425,17 +475,17 @@
```ruby
# with httpx
require "httpx"
response = httpx.post("https://auth_server/oauth-token",json: {
- client_id: ENV["OAUTH_CLIENT_ID"],
- client_secret: ENV["OAUTH_CLIENT_SECRET"],
+ client_id: env["oauth_client_id"],
+ client_secret: env["oauth_client_secret"],
grant_type: "authorization_code",
code: "oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as"
})
response.raise_for_status
-payload = JSON.parse(response.to_s)
+payload = json.parse(response.to_s)
puts payload #=> {
# "access_token" => ....
# "mac_key" => ....
# "mac_algorithm" =>
```
@@ -467,11 +517,10 @@
This will, by default, use the OAuth application as HMAC signature and "HS256" as the algorithm to sign the resulting JWT access tokens. You can tweak those features by editing the following options:
```ruby
enable :oauth_jwt
oauth_jwt_secret "SECRET"
-# or oauth_jwt_secret_path "path/to/file"
oauth_jwt_algorithm "HS512"
```
You can look for other options in [the jwt gem documentation](https://github.com/jwt/ruby-jwt), as this is used under the hood.
@@ -484,24 +533,28 @@
rsa_public = rsa_private.public_key
plugin :rodauth do
enable :oauth_jwt
oauth_jwt_key rsa_private
- oauth_jwt_decoding_secret rsa_public
+ oauth_jwt_public_key rsa_public
oauth_jwt_algorithm "RS256"
end
```
#### JWK
One can further encode the JWT token using JSON Web Keys. Here's how you could enable the feature:
```ruby
+rsa_private = OpenSSL::PKey::RSA.generate 2048
+rsa_public = rsa_private.public_key
+
plugin :rodauth do
enable :oauth_jwt
- oauth_jwt_jwk_key 2048 # can be key size or the encoded RSA key, which is the only one supported now.
- # oauth_jwt_jwk_key "path/to/rsa.pem" if you prefer
+ oauth_jwt_jwk_key rsa_private
+ oauth_jwt_jwk_public_key rsa_public
+ oauth_jwt_jwk_algorithm "RS256"
end
```
#### JWE
@@ -518,9 +571,29 @@
end
```
which adds an extra layer of protection.
+#### JWKS URI
+
+A route is defined for getting the JWK Set in a JSON format; this is typically used by client applications, who need the JWK set to decode the JWT token. This URL is typically `https://oauth-server/oauth-jwks`.
+
+#### JWT Bearer as authorization grant
+
+One can emit a new access token by using the bearer access token as grant. This can be done emitting a request similar to this:
+
+```ruby
+# with httpx
+require "httpx"
+response = httpx.post("https://auth_server/oauth-token",json: {
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
+ assertion: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6IkV4YW1wbGUiLCJpYXQiOjE1OTIwMDk1MDEsImNsaWVudF9pZCI6IkNMSUVOVF9JRCIsImV4cCI6MTU5MjAxMzEwMSwiYXVkIjpudWxsLCJzY29wZSI6InVzZXIucmVhZCB1c2VyLndyaXRlIiwianRpIjoiOGM1NTVjMjdiOWRjNDdmOTcyNWRkYzBhMjk0NzA1ZTA4NzFkY2JlN2Q5ZTNlMmVkNGE1ZTBiOGZlNTZlYzcxMSJ9.AlxKRtE3ec0mtyBSDx4VseND4eC6cH5ubtv8gfYxxsc"
+ })
+response.raise_for_status
+payload = json.parse(response.to_s)
+puts payload #=> {
+# "access_token" => "ey....
+```
#### DB Schema
You'll still need the "oauth_tokens" table, however you can remove the "token" column.