Skip to content

Instantly share code, notes, and snippets.

@mscharley
Created November 2, 2018 23:58
Show Gist options
  • Save mscharley/c82180cb4afa9fd2b94e460ed2f6fe01 to your computer and use it in GitHub Desktop.
Save mscharley/c82180cb4afa9fd2b94e460ed2f6fe01 to your computer and use it in GitHub Desktop.
Sample Session handler for bs-auth0-js
open Belt.Option;
open Js.Nullable;
open BsQuerystringify.QueryStringify;
open BsAuth0Js;
type accessToken = string;
type id = {
email: option(string),
name: option(string),
picture: option(string),
userId: string,
}
and loginEvent = {
accessToken,
id,
session: t,
returnUrl: option(string),
}
and errorEvent = {
error: string,
errorDescription: string,
}
and options = {
auth: WebAuth.t,
origin: string,
onLogin: loginHandler,
onRenew: loginHandler,
onError: errorHandler,
}
and loginHandler = loginEvent => unit
and errorHandler = errorEvent => unit
and t =
| Pending(options)
| LoggedOut(options)
| Session(options, string, id);
let create =
(
~domain,
~clientId,
~origin,
~scope="openid profile email",
~onLogin=_ => (),
~onRenew=_ => (),
~onError=_ => (),
(),
) => {
let auth =
WebAuth.createWebAuth(
WebAuth.createOptions(
~domain,
~clientID=clientId,
~responseType="token id_token",
~scope,
(),
),
);
Pending({auth, origin, onLogin, onRenew, onError});
};
let idFromWebauthPayload = payload => {
userId: payload->WebAuth.subGet,
email: payload->WebAuth.emailGet,
name: payload->WebAuth.nameGet,
picture: payload->WebAuth.pictureGet,
};
let isLoggedIn = s =>
switch (s) {
| Session(_, _, _) => true
| _ => false
};
let isPending = s =>
switch (s) {
| Pending(_) => true
| _ => false
};
let updateState = (s, update) =>
switch (s) {
| Pending(state) => Pending(state->update)
| LoggedOut(state) => LoggedOut(state->update)
| Session(state, accessToken, id) =>
Session(state->update, accessToken, id)
};
let onLogin = (s, handler) =>
s->updateState(state => {...state, onLogin: handler});
let onRenew = (s, handler) =>
s->updateState(state => {...state, onRenew: handler});
let onError = (s, handler) =>
s->updateState(state => {...state, onError: handler});
let rec handleAuthResult = (state, success, err, result) =>
switch (err->toOption, result->toOption) {
| (None, None) =>
state.onError({
error: "missing_token",
errorDescription: "There was an error with the authentication server.",
})
| (Some(e), _) =>
state.onError({
error: e->WebAuth.errorGet,
errorDescription: e->WebAuth.errorDescriptionGet,
})
| (_, Some(result)) =>
switch (result->WebAuth.accessTokenGet, result->WebAuth.idTokenPayloadGet) {
| (None, _)
| (_, None) =>
state.onError({
error: "missing_token",
errorDescription: "There was an error with the authentication server.",
})
| (Some(accessToken), Some(idToken)) =>
let id = idToken->idFromWebauthPayload;
let search = parseQueryString(Util.locationSearch);
let event = {
accessToken,
id,
session: Session(state, accessToken, id),
returnUrl: search->Js.Dict.get("return"),
};
let expiryMs = (result->WebAuth.expiresInGet->int_of_float - 120) * 1000;
Js.log2("Setting up token refresh timeout for", expiryMs);
Js.Global.setTimeout(() => event.session->doRenew, expiryMs) |> ignore;
success(event);
}
}
and doRenew = s =>
switch (s) {
| Pending(state)
| LoggedOut(state) =>
state.auth
->WebAuth.renewAuthWithOptions(
WebAuth.renewOptions(
~redirectUri=state.origin ++ "/oauth/callback?renew=true",
(),
),
state->handleAuthResult(state.onLogin),
)
| Session(state, _, _) =>
state.auth
->WebAuth.renewAuthWithOptions(
WebAuth.renewOptions(
~redirectUri=state.origin ++ "/oauth/callback?renew=true",
(),
),
state->handleAuthResult(state.onRenew),
)
};
let doLogin = (s, ~returnUrl=?, ()) =>
switch (s) {
| Pending(state)
| LoggedOut(state) =>
state.auth
->WebAuth.authorizeWithOptions(
WebAuth.authorizeOptions(
~redirectUri=
state.origin
++ "/oauth/callback"
++ returnUrl->map(s => "?return=" ++ s)->getWithDefault(""),
(),
),
)
| Session(_, _, _) => ()
};
let doLogout =
fun
| Session({auth, origin}, _, _) =>
auth->WebAuth.logoutWithOptions(
WebAuth.logoutOptions(~returnTo=origin ++ "/", ()),
)
| Pending(_)
| LoggedOut(_) => ();
let doLocalLogout =
fun
| Session(state, _, _)
| Pending(state)
| LoggedOut(state) => LoggedOut(state);
let doCallback =
fun
| Session(state, _, _) =>
state.auth->WebAuth.parseHash(handleAuthResult(state, state.onRenew))
| Pending(state)
| LoggedOut(state) =>
state.auth->WebAuth.parseHash(handleAuthResult(state, state.onLogin));
let accessTokenGet =
fun
| Session(_, accessToken, _) => Some(accessToken)
| _ => None;
let idGet =
fun
| Session(_, _, id) => Some(id)
| _ => None;
type t;
type accessToken;
type id = {
email: option(string),
name: option(string),
picture: option(string),
userId: string,
};
type loginEvent = {
accessToken,
id,
session: t,
returnUrl: option(string),
};
type errorEvent = {
error: string,
errorDescription: string,
};
type loginHandler = loginEvent => unit;
type errorHandler = errorEvent => unit;
let create:
(
~domain: string,
~clientId: string,
~origin: string,
~scope: string=?,
~onLogin: loginHandler=?,
~onRenew: loginHandler=?,
~onError: errorHandler=?,
unit
) =>
t;
let isLoggedIn: t => bool;
let isPending: t => bool;
let onLogin: (t, loginHandler) => t;
let onRenew: (t, loginHandler) => t;
let onError: (t, errorHandler) => t;
let doLogin: (t, ~returnUrl: string=?, unit) => unit;
let doRenew: t => unit;
let doLogout: t => unit;
let doLocalLogout: t => t;
let doCallback: t => unit;
let accessTokenGet: t => option(accessToken);
let idGet: t => option(id);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment