diff options
Diffstat (limited to 'fig-frontend-client/src')
| -rw-r--r-- | fig-frontend-client/src/Auth.js | 45 | ||||
| -rw-r--r-- | fig-frontend-client/src/Auth.purs | 29 | ||||
| -rw-r--r-- | fig-frontend-client/src/Config.js | 2 | ||||
| -rw-r--r-- | fig-frontend-client/src/Config.purs | 2 | ||||
| -rw-r--r-- | fig-frontend-client/src/Main.purs | 23 |
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 |
