README.md in rodauth-oauth-0.0.6 vs README.md in rodauth-oauth-0.1.0

- old
+ new

@@ -1,15 +1,15 @@ # Rodauth::Oauth -[![pipeline status](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/commits/master) -[![coverage report](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/coverage.svg)](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/commits/master) +[![pipeline status](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/pipelines?page=1&ref=master) +[![coverage report](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/coverage.svg)](https://honeyryderchuck.gitlab.io/rodauth-oauth/coverage/#_AllFiles) This is an extension to the `rodauth` gem which implements the [OAuth 2.0 framework](https://tools.ietf.org/html/rfc6749) for an authorization server. ## Features -This gem implements: +This gem implements the following RFCs and features of OAuth: * [The OAuth 2.0 protocol framework](https://tools.ietf.org/html/rfc6749): * [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); @@ -22,10 +22,12 @@ * [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); * [JWT Secured Authorization Requests](https://tools.ietf.org/html/draft-ietf-oauth-jwsreq-20); * OAuth application and token management dashboards; +It also implements the [OpenID Connect layer](https://openid.net/connect/) on top of the OAuth features it provides. + This gem supports also rails (through [rodauth-rails]((https://github.com/janko/rodauth-rails))). ## Installation @@ -41,10 +43,19 @@ Or install it yourself as: $ gem install rodauth-oauth + +## Resources +| | | +| ------------- | ----------------------------------------------------------- | +| Website | https://honeyryderchuck.gitlab.io/rodauth-oauth/ | +| Documentation | https://honeyryderchuck.gitlab.io/rodauth-oauth/rdoc/ | +| Wiki | https://gitlab.com/honeyryderchuck/rodauth-oauth/wikis/home | +| CI | https://gitlab.com/honeyryderchuck/rodauth-oauth/pipelines | + ## Usage This tutorial assumes you already read the documentation and know how to set up `rodauth`. After that, integrating `roda-auth` will look like: ```ruby @@ -84,12 +95,23 @@ end end end ``` -You'll have to do a bit more boilerplate, so here's the instructions. +For OpenID, it's very similar to the example above: + +```ruby +plugin :rodauth do + # enable it in the plugin + enable :login, :openid + oauth_application_default_scope %w[openid] + oauth_application_scopes %w[openid email profile] +end +``` + + ### Example (TL;DR) If you're familiar with the technology and want to skip the next paragraphs, just [check our example applications](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/tree/master/examples/). @@ -99,11 +121,11 @@ ##### HTTPX ```ruby require "httpx" -response = HTTPX.post("https://auth_server/oauth-token",json: { +response = HTTPX.post("https://auth_server/token",json: { client_id: ENV["OAUTH_CLIENT_ID"], client_secret: ENV["OAUTH_CLIENT_SECRET"], grant_type: "authorization_code", code: "oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as" }) @@ -113,22 +135,22 @@ ``` ##### cURL ``` -> curl --data '{"client_id":"$OAUTH_CLIENT_ID","client_secret":"$OAUTH_CLIENT_SECRET","grant_type":"authorization_code","code":"oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as"}' https://auth_server/oauth-token +> curl --data '{"client_id":"$OAUTH_CLIENT_ID","client_secret":"$OAUTH_CLIENT_SECRET","grant_type":"authorization_code","code":"oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as"}' https://auth_server/token ``` #### Refresh Token Refreshing expired tokens also happens mostly server-to-server, here's an example: ##### HTTPX ```ruby require "httpx" -response = HTTPX.post("https://auth_server/oauth-token",json: { +response = HTTPX.post("https://auth_server/token",json: { client_id: ENV["OAUTH_CLIENT_ID"], client_secret: ENV["OAUTH_CLIENT_SECRET"], grant_type: "refresh_token", token: "2r89hfef4j9f90d2j2390jf390g" }) @@ -138,22 +160,22 @@ ``` ##### cURL ``` -> curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","client_secret":"$OAUTH_CLIENT_SECRET","grant_type":"token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/oauth-token +> curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","client_secret":"$OAUTH_CLIENT_SECRET","grant_type":"token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/token ``` #### Revoking tokens 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(:basic_authorization) response = httpx.basic_authentication(ENV["CLIENT_ID"], ENV["CLIENT_SECRET"]) - .post("https://auth_server/oauth-revoke",json: { + .post("https://auth_server/revoke",json: { token_type_hint: "access_token", # can also be "refresh:tokn" token: "2r89hfef4j9f90d2j2390jf390g" }) response.raise_for_status payload = JSON.parse(response.to_s) @@ -161,22 +183,22 @@ ``` ##### 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 +> curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","token_type_hint":"access_token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/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: { + .post("https://auth_server/introspect",json: { token_type_hint: "access_token", # can also be "refresh:tokn" token: "2r89hfef4j9f90d2j2390jf390g" }) response.raise_for_status payload = JSON.parse(response.to_s) @@ -184,11 +206,11 @@ ``` ##### 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 +> curl -H "X-your-auth-scheme: $SERVER_KEY" --data '{"client_id":"$OAUTH_CLIENT_ID","token_type_hint":"access_token","token":"2r89hfef4j9f90d2j2390jf390g"}' https://auth_server/revoke ``` ### Authorization Server Metadata The Authorization Server Metadata endpoint can be used by clients to obtain the information needed to interact with an @@ -241,14 +263,14 @@ ### Endpoints Once you set it up, by default, the following endpoints will be available: -* `GET /oauth-authorize`: Loads the OAuth authorization HTML form; -* `POST /oauth-authorize`: Responds to an OAuth authorization request, as [per the spec](https://tools.ietf.org/html/rfc6749#section-4); -* `POST /oauth-token`: Generates OAuth tokens as [per the spec](https://tools.ietf.org/html/rfc6749#section-4.4.2); -* `POST /oauth-revoke`: Revokes OAuth tokens as [per the spec](https://tools.ietf.org/html/rfc7009); +* `GET /authorize`: Loads the OAuth authorization HTML form; +* `POST /authorize`: Responds to an OAuth authorization request, as [per the spec](https://tools.ietf.org/html/rfc6749#section-4); +* `POST /token`: Generates OAuth tokens as [per the spec](https://tools.ietf.org/html/rfc6749#section-4.4.2); +* `POST /revoke`: Revokes OAuth tokens as [per the spec](https://tools.ietf.org/html/rfc7009); ### OAuth applications This feature is **optional**, as not all authorization servers will want a full oauth applications dashboard. However, if you do and you don't want to do the work yourself, you can set it up in your roda app like this: @@ -424,11 +446,11 @@ The "Proof Key for Code Exchange by OAuth Public Clients" (aka PKCE) flow, which is **particularly recommended for OAuth integration in mobile apps**, is transparently supported by `rodauth-oauth`, by adding the `code_challenge_method=S256&code_challenge=$YOUR_CODE_CHALLENGE` query params to the authorization url. Once you do that, you'll have to pass the `code_verifier` when generating a token: ```ruby # with httpx require "httpx" -response = HTTPX.post("https://auth_server/oauth-token",json: { +response = HTTPX.post("https://auth_server/token",json: { client_id: ENV["OAUTH_CLIENT_ID"], grant_type: "authorization_code", code: "oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as", code_verifier: your_code_verifier_here }) @@ -475,11 +497,11 @@ Generating an access token will deliver the following fields: ```ruby # with httpx require "httpx" -response = httpx.post("https://auth_server/oauth-token",json: { +response = httpx.post("https://auth_server/token",json: { client_id: env["oauth_client_id"], client_secret: env["oauth_client_secret"], grant_type: "authorization_code", code: "oiweicnewdh32fhoi3hf3ihfo2ih3f2o3as" }) @@ -574,19 +596,19 @@ 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`. +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/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: { +response = httpx.post("https://auth_server/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)