Skip to content

Commit

Permalink
add favicon to help find tabs to close
Browse files Browse the repository at this point in the history
  • Loading branch information
timothypratley committed Jul 14, 2024
1 parent 14aad27 commit c0677d2
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 42 deletions.
2 changes: 1 addition & 1 deletion HappyAPI.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,18 @@ and then from a file `happyapi.edn`.
When no port is specified (for example `:redirect_uri "http://localhost/redirect"`), HappyAPI listens on the default http port 80.

Port 80 is a privileged port that requires root permissions, which may be problematic for some users.
Google allows the `redirect_uri` port to vary.
Google and GitHub allow the `redirect_uri` port to vary.
Other providers do not.
A random port is a natural choice.
Configuring `:redirect_uri "http://localhost:0/redirect"` will listen on a random port.
This is the default used for Google if not configured otherwise.
This is the default used for Google and GitHub if not configured otherwise.

You can choose a port if you'd like.
If you want to listen on port 8080, configure `:redirect_uri "http://localhost:8080/redirect"`
You need to update your provider settings to match.
Most providers require an exact match between the provider side settings and client config,
This is the default used for Twitter if not configured otherwise.

You must update your provider settings to match either the default, or your own `redirect_uri`.
Providers require an exact match between the provider side settings and client config,
so please check this carefully if you get an error.

### Instrumentation, logging, and metrics
Expand Down
2 changes: 1 addition & 1 deletion deps.edn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{:paths ["src"]
{:paths ["src" "resources"]

:deps {org.clojure/clojure {:mvn/version "1.11.3"}
buddy/buddy-sign {:mvn/version "3.5.351"}
Expand Down
1 change: 1 addition & 0 deletions docs/favicon.ico
1 change: 1 addition & 0 deletions docs/index.md
Binary file added resources/favicon.ico
Binary file not shown.
13 changes: 10 additions & 3 deletions src/happyapi/middleware.clj
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,17 @@
keywordize-keys (wrap-keywordize-keys)))

;; TODO: surely there are other cases to consider?
(defn remove-redundant-data-labels [x]
(if (map? x)
(cond (contains? x :data) (recur (get x :data))
(contains? x "data") (recur (get x "data"))
(seq (get x :items)) (get x :items)
(seq (get x "items")) (get x "items")
:else x)
x))

(defn extract-result [{:keys [body]}]
(cond (and (map? body) (seq (get body :items))) (get body :items)
(and (map? body) (seq (get body "items"))) (get body "items")
:else body))
(remove-redundant-data-labels body))

(defn wrap-extract-result
"When we call an API, we want the logical result of the call, not the map containing body, and status.
Expand Down
27 changes: 17 additions & 10 deletions src/happyapi/oauth2/capture_redirect.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
If you are making a web app, implement a route in your app that captures the code parameter.
If you use this namespace, add ring as a dependency in your project."
(:require [clojure.java.browse :as browse]
[clojure.java.io :as io]
[clojure.set :as set]
[happyapi.middleware :as middleware]
[happyapi.oauth2.auth :as oauth2]
[ring.middleware.params :as params]))
[ring.middleware.params :as params])
(:import (java.io FileInputStream)))

(set! *warn-on-reflection* true)

Expand All @@ -18,6 +20,18 @@
(-> (oauth2/provider-login-url config scopes optional)
(browse/browse-url)))

(defn make-redirect-handler [p]
(-> (fn redirect-handler [{:as req :keys [request-method uri params]}]
(case [request-method uri]
[:get "/favicon.ico"] {:body (io/file (io/resource "favicon.ico"))
:status 200}
(if (get @(deliver p params) "code")
{:status 200
:body "Code received, authentication successful."}
{:status 400
:body "No code in response."})))
(params/wrap-params)))

(defn fresh-credentials
"Opens a browser to authenticate, waits for a redirect, and returns a code.
Defaults access_type to offline,
Expand All @@ -39,15 +53,8 @@
port (if requested-port
(Integer/parseInt requested-port)
80)
http-redirect-handler (fn [request]
(if (get @(deliver p (get request :params)) "code")
{:status 200
:body "Code received, authentication successful."}
{:status 400
:body "No code in response."}))
handler (params/wrap-params http-redirect-handler)
{:keys [run-server]} fns
{:keys [port stop]} (run-server handler {:port port})
{:keys [port stop]} (run-server (make-redirect-handler p) {:port port})
;; The port may have changed when requesting a random port
config (if requested-port
(assoc config :redirect_uri (str protocol host ":" port path))
Expand All @@ -70,7 +77,7 @@
;; wait for the user to get redirected to localhost with a code
{:strs [code state] :as return-params} (deref p login-timeout nil)]
;; allow a bit of time to deliver the response before shutting down the server
(stop)
(Thread. (fn [] (Thread/sleep 1000) (stop)))
(if code
(do
(when-not (= state state-and-challenge)
Expand Down
9 changes: 6 additions & 3 deletions src/happyapi/oauth2/client.clj
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@
(defmethod endpoints :google [_]
{:auth_uri "https://accounts.google.com/o/oauth2/auth"
:token_uri "https://oauth2.googleapis.com/token"
;; port 0 indicates random port
;; port 0 selects a random port
:redirect_uri "http://localhost:0/redirect"
:authorization_options {:access_type "offline"
:prompt "consent"
:include_granted_scopes true}})
(defmethod endpoints :github [_]
{:auth_uri "https://github.com/login/oauth/authorize"
:token_uri "https://github.com/login/oauth/access_token"})
{:auth_uri "https://github.com/login/oauth/authorize"
:token_uri "https://github.com/login/oauth/access_token"
;; port 0 selects a random port
:redirect_uri "http://localhost:0/redirect"})
(defmethod endpoints :twitter [_]
{:auth_uri "https://twitter.com/i/oauth2/authorize"
:token_uri "https://api.twitter.com/2/oauth2/token"
:redirect_uri "http://localhost:8080/redirect"
:authorization_options {:code_challenge_method "plain"}})

(defn with-endpoints
Expand Down
16 changes: 10 additions & 6 deletions test/happyapi/oauth2/capture_redirect_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@
{:auth_uri "TEST"
:client_id "TEST"
:redirect_uri "http://localhost"}
[]
{})))
[])))
(is (= {:access_token "TOKEN"}
(capture-redirect/fresh-credentials http/request
{:auth_uri "TEST"
:client_id "TEST"
:redirect_uri "http://localhost:8080/redirect"}
[]
{})))
[])))
(is (thrown? Throwable
(capture-redirect/fresh-credentials http/request
{:auth_uri "TEST"
:client_id "TEST"
:redirect_uri "http://not.localhost"}
[]
{})))))
[])))))

(deftest make-redirect-handler-test
(let [p (promise)]

(capture-redirect/make-redirect-handler p)
()
))
27 changes: 13 additions & 14 deletions test/happyapi/providers/twitter_test.clj
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
(ns happyapi.providers.twitter-test
(:require [clojure.edn :as edn]
[clojure.test :refer :all]
(:require [clojure.test :refer :all]
[happyapi.providers.twitter :as twitter]))

(deftest api-request-test
(twitter/setup! (assoc-in (edn/read-string (slurp "happyapi.edn"))
[:twitter :redirect_uri]
"http://localhost:8080/redirect"))
(twitter/api-request {:method :get
:url "https://api.twitter.com/2/users/me"
:scopes ["tweet.read" "tweet.write" "users.read"]})
(twitter/api-request {:method :delete
:url "https://api.twitter.com/2/tweets/1811986925798195513"
:scopes ["tweet.read" "tweet.write" "users.read"]})
(twitter/setup! nil)
(is (-> (twitter/api-request {:method :get
:url "https://api.twitter.com/2/users/me"
:scopes ["tweet.read" "tweet.write" "users.read"]})
:username))
(is (-> (twitter/api-request {:method :delete
:url "https://api.twitter.com/2/tweets/1811986925798195513"
:scopes ["tweet.read" "tweet.write" "users.read"]})
:deleted))
;; let's not post every time I run the tests...
#_(twitter/api-request {:method :post
:url "https://api.twitter.com/2/tweets"
:scopes ["tweet.read" "tweet.write" "users.read"]
:body {:text "This is a test tweet from HappyAPI"}}))
:url "https://api.twitter.com/2/tweets"
:scopes ["tweet.read" "tweet.write" "users.read"]
:body {:text "This is a test tweet from HappyAPI"}}))

0 comments on commit c0677d2

Please sign in to comment.