diff --git a/.gitignore b/.gitignore index 0d20a0f..0640248 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,4 @@ pom.xml* *.jar *.class -.lein-deps-sum -.lein-failures -.lein-plugins -.lein-env +.lein-* diff --git a/dev-resources/src/friend_oauth2/dev.clj b/dev-resources/src/friend_oauth2/dev.clj new file mode 100644 index 0000000..5bfe121 --- /dev/null +++ b/dev-resources/src/friend_oauth2/dev.clj @@ -0,0 +1,2 @@ +(ns friend-oauth2.dev + (:require [friend-oauth2.config :as config])) diff --git a/docs/source/20-configurtion.md b/docs/source/20-configurtion.md index 8b6f3a2..9427036 100644 --- a/docs/source/20-configurtion.md +++ b/docs/source/20-configurtion.md @@ -1,8 +1,46 @@ # Configuration Reference +There are two ways in which one may configure use of `friend-oauth2`: + +* via two custom maps (`client-config` and `uri-config`), or +* via a single configuration record + +The latter is a new method and should not be considered for production use +yet. For those applications that can take a chance on this new feature, we'd +be greatly appreciative for usage, testing in the wild, and any bug reports! + + +## Legacy Configuration + The map passed to `oauth2/workflow` accepts various optional arguments, -described below. See also the [example handlers][2] for working examples. +described below. See also the code in `examples/src` for working examples. +Here's some code from the Github example: + +```clj +(def callback-url (System/getenv "OAUTH2_CALLBACK_URL")) +(def parsed-url (url/url callback-url)) + +(def client-config + {:client-id (System/getenv "OAUTH2_CLIENT_ID") + :client-secret (System/getenv "OAUTH2_CLIENT_SECRET") + :callback {:domain (format "%s://%s:%s" + (:protocol parsed-url) + (:host parsed-url) + (:port parsed-url)) + :path (:path parsed-url)}}) +(def uri-config + {:authentication-uri {:url "https://github.com/login/oauth/authorize" + :query {:client_id (:client-id client-config) + :response_type "code" + :redirect_uri callback-url + :scope "user"}} + :access-token-uri {:url "https://github.com/login/oauth/access_token" + :query {:client_id (:client-id client-config) + :client_secret (:client-secret client-config) + :grant_type "authorization_code" + :redirect_uri callback-url}}}) +``` * `client-config` holds the basic information which changes from app to app, regardless of the provider: `:client-id`, `:client-secret`, and the @@ -24,4 +62,174 @@ alternate function (`get-access-token-from-params`) supplied to handle the common case where an access_token is provided as parameters in the callback request. Simply set the `:access-token-parsefn get-access-token-from-params` See the -[Facebook and Github examples][2] for reference. +Facebook and Github examples for reference. + + +## New Configuration + +The new way of configuring `friend-oauth2` in an application makes use of the +`friend-oauth2.config/Client` record. Here is some example usage for creating +a new client configuration: + +```clj +(require '[friend-oauth2.config :as config] +(config/client + :scope "user" + :auth-uri "https://github.com/login/oauth/authorize" + :token-uri "https://github.com/login/oauth/access_token") +``` + +Note that if the `:client-id`, `:client-secret`, and `:redirect-uri` arguments +are not proivded, they are taken from the `OAUTH2_CLIENT_ID`, +`OAUTH2_CLIENT_SECRET`, and `OAUTH2_CALLBACK_URL` environment variables. + +The `Client` record defines the following +fields: + +* `client-id` +* `client-secret` +* `redirect-uri` +* `response-type` +* `scope` +* `state` +* `access-type` +* `prompt` +* `login-hint` +* `include-granted-scopes` +* `code` +* `grant-type` +* `adnview` +* `allow-signup` +* `auth-uri` +* `token-uri` + +We recommend using the constructor `friend-oauth2.config/client` which will +populate a client configuration record with default values when not specified +in the constructor. + +If they are not provided, they will be looked up in the system environment, +supplying those values instead. + +and these are the defaults: + +* `client-id`: `(System/getenv "OAUTH2_CLIENT_ID")` +* `client-secret`: `(System/getenv "OAUTH2_CLIENT_SECRET")` +* `redirect-uri`: `(System/getenv "OAUTH2_CALLBACK_URL")` +* `response-type`: `"code"` +* `grant-type`: `"authorization_code"` + +and these are set to `nil` by default: + +* `scope` +* `state` +* `access-type` +* `prompt` +* `login-hint` +* `include-granted-scopes` +* `adnview` +* `allow-signup` +* `auth-uri` +* `token-uri` + +Backwards compatibility with the legacy `friend-oauth2` configuration is +facilitated by several configuration utility functions that perform appropriate +tranformations from the record-based approach. + + +### Sources + +The fields for the record above are the combination of the fields taken from +the OAuth2 services supported by `friend-oauth2`. Each is covered below in its +own sub-section. + + +#### App.net + +[App.net's OAuth2 service][app.net service] supports the following query +string parameters for the authorization phase: + +* `client_id` +* `response_type` (value `code`) +* `redirect_uri` +* `scope` +* `state` +* `adnview` + +And it supports the following for access code exchange: + +* `code` +* `client_id` +* `client_secret` +* `redirect_uri` +* `grant_type` (value `authorization_code`) + + +#### Facebook + +[Facebook's OAuth2 service][facebook service] supports the following query +string parameters for the authorization phase: + +* `client_id` +* `response_type` (values `code`, `token`, `code%20token`, or + `granted_scopes`) +* `redirect_uri` +* `scope` +* `state` + +And it supports the following for access code exchange: + +* `code` +* `client_id` +* `client_secret` +* `redirect_uri` + + +#### Github + +[Github's OAuth2 service][github service] supports the following query +string parameters for the authorization phase: + +* `client_id` +* `redirect_uri` +* `scope` +* `state` +* `allow_signup` (values `true` or `false`) + +And it supports the following for access code exchange: + +* `code` +* `client_id` +* `client_secret` +* `redirect_uri` +* `state` + + +#### Google + +[Google's OAuth2 service][google oauth2 service] supports the following query +string parameters for the authorization phase: + +* `response_type` (value `code`) +* `client_id` +* `redirect_uri` +* `scope` +* `state` +* `access_type` (values `online` and `offline`) +* `prompt` (values `none`, `consent`, or `select_account`) +* `login_hint` +* `include_granted_scopes` (values `true` or `false`) + + +And it supports the following for access code exchange: + +* `code` +* `client_id` +* `client_secret` +* `redirect_uri` +* `grant_type` (value `authorization_code`) + + +[app.net service]: https://developers.app.net/reference/authentication/flows/web/#server-side-flow +[facebook service]: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow +[github service]: https://developer.github.com/v3/oauth/ +[google oauth2 service]: https://developers.google.com/identity/protocols/OAuth2WebServer diff --git a/docs/source/50-examples.md b/docs/source/50-examples.md index c5e1f41..7ae56d0 100644 --- a/docs/source/50-examples.md +++ b/docs/source/50-examples.md @@ -2,10 +2,10 @@ The friend-oauth2 project includes the following examples: + * [App.net](https://developers.app.net/reference/authentication/) + * [Facebook (server-side authentication)](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/) + * [Github](https://developer.github.com/v3/oauth/) * [Google OAuth 2.0 Login](https://developers.google.com/accounts/docs/OAuth2Login) - * [Facebook (server-side authentication)](https://developers.facebook.com/docs/authentication/server-side/) - * [App.net](https://github.com/appdotnet/api-spec/blob/master/auth.md) - * [Github](http://developer.github.com/v3/oauth/) ## Configuration diff --git a/src/friend_oauth2/config.clj b/src/friend_oauth2/config.clj new file mode 100644 index 0000000..9884176 --- /dev/null +++ b/src/friend_oauth2/config.clj @@ -0,0 +1,60 @@ +(ns friend-oauth2.config) + +(defrecord Client + [client-id + client-secret + redirect-uri + response-type + scope + state + access-type + prompt + login-hint + include-granted-scopes + code + grant-type + adnview + allow-signup + auth-uri + token-uri]) + +(defn client + [& { + :keys [client-id + client-secret + redirect-uri + response-type + scope + state + access-type + prompt + login-hint + include-granted-scopes + code + grant-type + adnview + allow-signup + auth-uri + token-uri] + :or {client-id (System/getenv "OAUTH2_CLIENT_ID") + client-secret (System/getenv "OAUTH2_CLIENT_SECRET") + redirect-uri (System/getenv "OAUTH2_CALLBACK_URL") + response-type "code" + grant-type "authorization_code"}}] + (->Client + client-id + client-secret + redirect-uri + response-type + scope + state + access-type + prompt + login-hint + include-granted-scopes + code + grant-type + adnview + allow-signup + auth-uri + token-uri)) diff --git a/src/friend_oauth2/workflow.clj b/src/friend_oauth2/workflow.clj index 2696429..11128d1 100644 --- a/src/friend_oauth2/workflow.clj +++ b/src/friend_oauth2/workflow.clj @@ -60,7 +60,7 @@ (s/defn ^:always-validate workflow "Workflow for OAuth2" [config :- {(s/required-key :client-config) ClientConfig - s/Any s/Any}];; The rest of config. + s/Any s/Any}] ;; The rest of config. (fn [request] (when (is-oauth2-callback? config request) ;; Extracts code from request if we are getting here via OAuth2 callback.