summaryrefslogtreecommitdiff
path: root/fig-frontend-client/src
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2024-08-08 21:52:29 -0400
committerLLLL Colonq <llll@colonq>2024-08-08 21:52:29 -0400
commit31d0954b9e51a0ca9071a92637e3a2e86660fe3e (patch)
treeb8cfa0aecb709991c9ca8a9fca572814b74dac74 /fig-frontend-client/src
parent2482292d033013ff37bbd4cdac00632b3dc70323 (diff)
Auth for frontend
Diffstat (limited to 'fig-frontend-client/src')
-rw-r--r--fig-frontend-client/src/Auth.js45
-rw-r--r--fig-frontend-client/src/Auth.purs29
-rw-r--r--fig-frontend-client/src/Config.js2
-rw-r--r--fig-frontend-client/src/Config.purs2
-rw-r--r--fig-frontend-client/src/Main.purs23
5 files changed, 100 insertions, 1 deletions
diff --git a/fig-frontend-client/src/Auth.js b/fig-frontend-client/src/Auth.js
new file mode 100644
index 0000000..7254c40
--- /dev/null
+++ b/fig-frontend-client/src/Auth.js
@@ -0,0 +1,45 @@
+function generateNonce() {
+ var arr = new Uint8Array(20);
+ window.crypto.getRandomValues(arr);
+ return Array.from(arr, b => b.toString(16).padStart(2, "0")).join("");
+}
+
+export const _startTwitchAuth = (clientID) => (redirectURL) => () => {
+ const nonce = generateNonce();
+ document.cookie = `authnonce=${nonce}; path=/; max-age=3000`;
+ window.location.href =
+ `https://id.twitch.tv/oauth2/authorize?response_type=id_token`
+ + `&client_id=${clientID}`
+ + `&redirect_uri=${redirectURL}`
+ + `&scope=openid`
+ + `&nonce=${nonce}`
+ + `&claims=${JSON.stringify({id_token: {preferred_username: null}})}`
+ ;
+};
+
+function getFragmentQuery() {
+ let query = new Map();
+ const hashQuery = document.location.hash.slice(1).split("&");
+ for (let equals of hashQuery) {
+ const pair = equals.split("=");
+ query.set(decodeURIComponent(pair[0]), decodeURIComponent(pair[1]));
+ }
+ return query;
+}
+
+export const _getToken = (Just) => (Nothing) => (pair) => () => {
+ const frag = getFragmentQuery();
+ const token = frag.get("id_token");
+ if (token) {
+ document.cookie = `id_token=${token}; path=/; SameSite=Strict`;
+ }
+ let id_token = null;
+ let authnonce = null;
+ for (let c of document.cookie.split("; ")) {
+ const [k, v] = c.split("=");
+ if (k === "id_token") id_token = v;
+ else if (k === "authnonce") authnonce = v;
+ }
+ if (id_token && authnonce) return Just(pair(id_token)(authnonce));
+ return Nothing;
+};
diff --git a/fig-frontend-client/src/Auth.purs b/fig-frontend-client/src/Auth.purs
new file mode 100644
index 0000000..2a53629
--- /dev/null
+++ b/fig-frontend-client/src/Auth.purs
@@ -0,0 +1,29 @@
+module Auth where
+
+import Prelude
+
+import Config (authRedirectURL, clientID)
+import Data.Array (fold)
+import Data.Maybe (Maybe(..))
+import Data.Tuple (Tuple(..))
+import Effect (Effect)
+import Effect.Class (class MonadEffect, liftEffect)
+
+foreign import _startTwitchAuth :: String -> String -> Effect Unit
+startTwitchAuth :: forall m. MonadEffect m => m Unit
+startTwitchAuth = liftEffect $ _startTwitchAuth clientID authRedirectURL
+
+type AuthInfo = Tuple String String
+foreign import _getToken :: forall a. (a -> Maybe a) -> Maybe a -> (a -> a -> Tuple a a) -> Effect (Maybe (Tuple String String))
+getToken :: forall m. MonadEffect m => m (Maybe AuthInfo)
+getToken = liftEffect $ _getToken Just Nothing Tuple
+
+authHeader :: AuthInfo -> String
+authHeader (Tuple t n) =
+ fold
+ [ "FIG-TWITCH token=\""
+ , t
+ , "\", nonce=\""
+ , n
+ , "\""
+ ]
diff --git a/fig-frontend-client/src/Config.js b/fig-frontend-client/src/Config.js
index 0990a53..25259c7 100644
--- a/fig-frontend-client/src/Config.js
+++ b/fig-frontend-client/src/Config.js
@@ -1,2 +1,4 @@
export const mode = globalThis.mode;
export const apiServer = globalThis.apiServer;
+export const clientID = globalThis.clientID;
+export const authRedirectURL = globalThis.authRedirectURL;
diff --git a/fig-frontend-client/src/Config.purs b/fig-frontend-client/src/Config.purs
index 3711d69..536f163 100644
--- a/fig-frontend-client/src/Config.purs
+++ b/fig-frontend-client/src/Config.purs
@@ -2,3 +2,5 @@ module Config where
foreign import mode :: Int
foreign import apiServer :: String
+foreign import clientID :: String
+foreign import authRedirectURL :: String
diff --git a/fig-frontend-client/src/Main.purs b/fig-frontend-client/src/Main.purs
index 8b132a7..48a6e9d 100644
--- a/fig-frontend-client/src/Main.purs
+++ b/fig-frontend-client/src/Main.purs
@@ -3,12 +3,14 @@ module Main where
import Prelude
import Audio as Audio
+import Auth (AuthInfo, authHeader, getToken, startTwitchAuth)
import Config as Config
import Data.Array (head)
import Data.Array as Array
import Data.Foldable (fold)
import Data.Maybe (Maybe(..))
import Data.Traversable (for, for_)
+import Data.Tuple (Tuple(..))
import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Effect.Class (class MonadEffect, liftEffect)
@@ -19,13 +21,14 @@ import Model (startModel)
import UI as UI
import Web.DOM as DOM
import Web.DOM.DOMTokenList as DOM.DTL
+import Web.DOM.Document (doctype)
import Web.DOM.Document as DOM.Doc
import Web.DOM.Element as DOM.El
import Web.DOM.Node as DOM.Node
import Web.DOM.NodeList as DOM.NL
import Web.DOM.NonElementParentNode as DOM.NEP
-import Web.DOM.Text as DOM.Text
import Web.DOM.ParentNode as DOM.P
+import Web.DOM.Text as DOM.Text
import Web.Event.Event as Ev
import Web.Event.EventTarget as Ev.Tar
import Web.HTML as HTML
@@ -94,6 +97,16 @@ updateSubtitle = do
{ text: catchphrase } <- fetch (Config.apiServer <> "/catchphrase") {}
catchphrase >>= setText subtitle
+checkAuth :: AuthInfo -> Aff String
+checkAuth auth = do
+ { text: resp } <-
+ fetch (Config.apiServer <> "/check")
+ { headers:
+ { "Authorization": authHeader auth
+ }
+ }
+ resp
+
mainHomepage :: Effect Unit
mainHomepage = launchAff_ do
liftEffect $ log "hi"
@@ -102,9 +115,17 @@ mainHomepage = launchAff_ do
{ text: motd } <- fetch (Config.apiServer <> "/motd") {}
motd >>= setText marq
+ getToken >>= case _ of
+ Just a@(Tuple t n) -> do
+ liftEffect $ log t
+ liftEffect $ log n
+ checkAuth a >>= log >>> liftEffect
+ _ -> pure unit
+
updateSubtitle
subtitle <- byId "lcolonq-subtitle"
listen subtitle "click" \_ev -> do
+ startTwitchAuth
launchAff_ updateSubtitle
for_ (Array.range 0 6) \i -> do