From fe048624c91592b706aa1b8d701f4780e526cd53 Mon Sep 17 00:00:00 2001 From: rawtoast Date: Sat, 31 Mar 2018 19:27:26 +0100 Subject: [PATCH] Minimal routing, required to handle callbacks --- dokusho/src/App.re | 32 +++++++++++++++--- dokusho/src/app/Dokusho.re | 2 +- dokusho/src/app/LoginButton.re | 59 ++++++++++++++++++++++++++++------ dokusho/src/app/Types.re | 25 ++++++++++++++ 4 files changed, 102 insertions(+), 16 deletions(-) diff --git a/dokusho/src/App.re b/dokusho/src/App.re index 03e8db8..802bf60 100644 --- a/dokusho/src/App.re +++ b/dokusho/src/App.re @@ -2,19 +2,41 @@ [%bs.raw {|require('../node_modules/auth0-js/build/auth0.js')|}]; [%bs.raw {|require('./toolbox/theme.css')|}]; -open Dokusho; - [@bs.module] external theme : ReactToolbox.ThemeProvider.theme = "./toolbox/theme"; -let component = ReasonReact.statelessComponent("App"); +open Dokusho; +open Types; + +type action = + | ChangeRoute(Routes.route); + +let reducer = (action, _state) => + switch action { + | ChangeRoute(route) => ReasonReact.Update( route ) + }; + +let component = ReasonReact.reducerComponent("App"); let make = _children => { ...component, - render: _self => + reducer, + initialState: () => { Routes.Home }, + render: self =>
- + (switch self.state { + | Routes.Home => + })
+}; + +let mapUrlToRoute = (url: ReasonReact.Router.url) => + switch url.path { + | [] => Routes.Home + | ["callback"] => { + LoginButton.Auth.handleAuth(url); + } + | _ => Routes.Home /* Routes.NotFound */ }; \ No newline at end of file diff --git a/dokusho/src/app/Dokusho.re b/dokusho/src/app/Dokusho.re index 4ab6779..033f7a5 100644 --- a/dokusho/src/app/Dokusho.re +++ b/dokusho/src/app/Dokusho.re @@ -9,7 +9,7 @@ open Rationale; module Dokusho { let component = ReasonReact.reducerComponent("Dokusho"); let initState = () => { - readingData: { days : [Day.now()] }, + readingData: { days : [ Day.now() ] }, selectedEntry: Book, selectedDate: Js.Date.make() }; diff --git a/dokusho/src/app/LoginButton.re b/dokusho/src/app/LoginButton.re index 9db5572..2590322 100644 --- a/dokusho/src/app/LoginButton.re +++ b/dokusho/src/app/LoginButton.re @@ -1,18 +1,57 @@ module Auth { - type generatedAuth0Client = {. - "authorize": [@bs.meth] (unit => unit) + type generatedAuth0Client = {. + "authorize": [@bs.meth] (unit => unit) + }; + + type clientOptions = { + . + "domain": string, + "clientID": string, + "redirectUri": string, + "responseType": string, + "scope": string }; - type clientOptions = { - . - "domain": string, - "clientID": string, - "redirectUri": string, - "responseType": string, - "scope": string + [@bs.module "auth0-js"] [@bs.new] external createClient : (clientOptions => generatedAuth0Client) = "WebAuth"; + + let matchAccessToken = [%re "/access_token=([^\$&]+)/g"]; + let matchExpiresIn = [%re "/expires_in=([^\$&]+)/g"]; + let matchIdToken = [%re "/id_token=([^\$&]+)/g"]; + + let resolveOption = (opt) => switch opt { + | None => "" + | Some(s) => s + }; + +let resolveRegex = (exp, str) => { + let res = exp |> Js.Re.exec(str); + switch res { + | None => "" + | Some(result) => { + let captures = result |> Js.Re.captures; + switch captures { + | [|_, token|] => token |> Js.Nullable.to_opt |> resolveOption + | _ => "" }; + }}; + }; + + open Types; + open Dom.Storage; + + let handleAuth = (url: ReasonReact.Router.url) => { + let accessToken = url.hash |> resolveRegex(matchAccessToken); + let idToken = url.hash |> resolveRegex(matchIdToken); + let expiresIn = url.hash |> resolveRegex(matchExpiresIn); + localStorage |> setItem("accessToken", accessToken); + localStorage |> setItem("id_token", idToken); + localStorage |> setItem("expiresIn", expiresIn); + + Routes.Home; + }; + + let getIdToken = () => localStorage |> getItem("id_token") |> resolveOption; - [@bs.module "auth0-js"] [@bs.new] external createClient : (clientOptions => generatedAuth0Client) = "WebAuth"; }; let authOptions = { diff --git a/dokusho/src/app/Types.re b/dokusho/src/app/Types.re index 48bac0b..bc6bbe5 100644 --- a/dokusho/src/app/Types.re +++ b/dokusho/src/app/Types.re @@ -1,6 +1,11 @@ /* TODO: Remove this for real authentication */ let testUser = "fully"; +module Routes { + type route = + | Home; +}; + type pageType = | Manga | News @@ -39,7 +44,19 @@ type action = | LoadUserData(string) | SelectDate(Js.Date.t); +type authData = { + accessToken: string, + idToken: string, + expiresIn: string +}; + module Decoders = { + let parseAuthData = (json: Js.Json.t) : authData => + Json.Decode.{ + accessToken: json |> field("accessToken", string), + idToken: json |> field("idToken", string), + expiresIn: json |> field("expiresIn", string) + }; let parsePageType = (asString:string) => { switch (asString) { | "Manga" => Manga @@ -68,6 +85,14 @@ module Decoders = { }; module Encoders = { + let encodeAuthData = authData => + Json.Encode.( + object_([ + ("accessToken", string(authData.accessToken)), + ("idToken", string(authData.idToken)), + ("expiresIn", string(authData.expiresIn)) + ]) + ); let encodeEntry = entry => Json.Encode.( object_([