Skip to content

Instantly share code, notes, and snippets.

@ciwchris
Last active November 17, 2021 13:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ciwchris/018bf8de33834a6933ca2f4a0b124572 to your computer and use it in GitHub Desktop.
Save ciwchris/018bf8de33834a6933ca2f4a0b124572 to your computer and use it in GitHub Desktop.
Elm auth
elm-stuff/
*.js
module Auth0
exposing
( AuthenticationState(..)
, AuthenticationError
, AuthenticationResult
, RawAuthenticationResult
, Options
, defaultOpts
, LoggedInUser
, UserProfile
, Token
, mapResult
)
type alias LoggedInUser =
{ profile : UserProfile
, token : Token
}
type AuthenticationState
= LoggedOut
| LoggedIn LoggedInUser
type alias Options =
{}
type alias UserProfile =
{ name : String
, nickname : String
, picture : String
, user_id : String
}
type alias Token =
String
type alias AuthenticationError =
{ name : Maybe String
, code : Maybe String
, description : String
, statusCode : Maybe Int
}
type alias AuthenticationResult =
Result AuthenticationError LoggedInUser
type alias RawAuthenticationResult =
{ err : Maybe AuthenticationError
, ok : Maybe LoggedInUser
}
mapResult : RawAuthenticationResult -> AuthenticationResult
mapResult result =
case ( result.err, result.ok ) of
( Just msg, _ ) ->
Err msg
( Nothing, Nothing ) ->
Err { name = Nothing, code = Nothing, statusCode = Nothing, description = "No information was received from the authentication provider" }
( Nothing, Just user ) ->
Ok user
defaultOpts : Options
defaultOpts =
{}
module Authentication
exposing
( Msg(..)
, Model
, init
, update
, handleAuthResult
, tryGetUserProfile
, isLoggedIn
)
import Auth0
type alias Model =
{ state : Auth0.AuthenticationState
, lastError : Maybe Auth0.AuthenticationError
, showLock : Auth0.Options -> Cmd Msg
, logOut : () -> Cmd Msg
}
init : (Auth0.Options -> Cmd Msg) -> (() -> Cmd Msg) -> Maybe Auth0.LoggedInUser -> Model
init showLock logOut initialData =
{ state =
case initialData of
Just user ->
Auth0.LoggedIn user
Nothing ->
Auth0.LoggedOut
, lastError = Nothing
, showLock = showLock
, logOut = logOut
}
type Msg
= AuthenticationResult Auth0.AuthenticationResult
| ShowLogIn
| LogOut
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
AuthenticationResult result ->
let
( newState, error ) =
case result of
Ok user ->
( Auth0.LoggedIn user, Nothing )
Err err ->
( model.state, Just err )
in
( { model | state = newState, lastError = error }, Cmd.none )
ShowLogIn ->
( model, model.showLock Auth0.defaultOpts )
LogOut ->
( { model | state = Auth0.LoggedOut }, model.logOut () )
handleAuthResult : Auth0.RawAuthenticationResult -> Msg
handleAuthResult =
Auth0.mapResult >> AuthenticationResult
tryGetUserProfile : Model -> Maybe Auth0.UserProfile
tryGetUserProfile model =
case model.state of
Auth0.LoggedIn user ->
Just user.profile
Auth0.LoggedOut ->
Nothing
isLoggedIn : Model -> Bool
isLoggedIn model =
case model.state of
Auth0.LoggedIn _ ->
True
Auth0.LoggedOut ->
False
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80 characters",
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "4.0.5 <= v < 5.0.0",
"elm-lang/html": "1.1.0 <= v < 2.0.0"
},
"elm-version": "0.17.1 <= v < 0.18.0"
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Elm with Auth0</title>
<script src="main.js"></script>
<script src="auth0.js"></script>
<script src="port.js"></script>
<script src="authentication.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body>
</body>
<script src="https://cdn.auth0.com/js/lock/10.0/lock.min.js"></script>
<script>
var lock = new Auth0Lock('7dibmFIZ7GbP8RAUQ5oQebcFkjHFG0l0', 'ciwchris.auth0.com', {
auth: {
redirectUrl: '',
responseType: 'token',
params: {
scope: 'openid email' // Learn about scopes: https://auth0.com/docs/scopes
}
}
});
var storedProfile = localStorage.getItem('profile');
var storedToken = localStorage.getItem('token');
var authData = storedProfile && storedToken ? { profile: JSON.parse(storedProfile), token: storedToken } : null;
console.dir(authData);
lock.on("authenticated", function(authResult) {
lock.getProfile(authResult.idToken, function(error, profile) {
var result = { err: null, ok: null };
if (error) {
result.err = error.details;
// Ensure that optional fields are on the object
result.err.name = result.err.name ? result.err.name : null;
result.err.code = result.err.code ? result.err.code : null;
result.err.statusCode = result.err.statusCode ? result.err.statusCode : null;
} else {
result.ok = { profile: profile, token: authResult.idToken };
localStorage.setItem('profile', JSON.stringify(profile));
localStorage.setItem('token', authResult.idToken);
}
// Update DOM
elmApp.ports.auth0authResult.send(result);
});
});
var elmApp = Elm.Main.fullscreen(authData);
elmApp.ports.auth0showLock.subscribe(function(opts) {
lock.show();
});
elmApp.ports.auth0logout.subscribe(function(opts) {
localStorage.removeItem('profile');
localStorage.removeItem('token');
});
</script>
</html>
module Main exposing (..)
import Html.App
import Html exposing (..)
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import Auth0
import Authentication
import Port exposing (..)
type alias Model =
{ authModel : Authentication.Model }
type Msg
= AuthenticationMsg Authentication.Msg
init : Maybe Auth0.LoggedInUser -> ( Model, Cmd Msg )
init initialUser =
( Model (Authentication.init auth0showLock auth0logout initialUser), Cmd.none )
view : Model -> Html Msg
view model =
div [ class "container" ]
[ div [ class "jumbotron text-center" ]
[ div []
(case Authentication.tryGetUserProfile model.authModel of
Nothing ->
[ p [] [ text "Please log in" ] ]
Just user ->
[ p [] [ img [ src user.picture ] [] ]
, p [] [ text ("Hello, " ++ user.name ++ "!") ]
]
)
, p []
[ button
[ class "btn btn-primary"
, onClick
(AuthenticationMsg
(if Authentication.isLoggedIn model.authModel then
Authentication.LogOut
else
Authentication.ShowLogIn
)
)
]
[ text
(if Authentication.isLoggedIn model.authModel then
"Logout"
else
"Login"
)
]
]
]
]
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
AuthenticationMsg authMsg ->
let
( authModel, cmd ) =
Authentication.update authMsg model.authModel
in
( { model | authModel = authModel }, Cmd.map AuthenticationMsg cmd )
subscriptions : a -> Sub Msg
subscriptions model =
auth0authResult (Authentication.handleAuthResult >> AuthenticationMsg)
main : Program (Maybe Auth0.LoggedInUser)
main =
Html.App.programWithFlags
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
port module Port exposing (..)
import Auth0
port auth0showLock : Auth0.Options -> Cmd msg
port auth0authResult : (Auth0.RawAuthenticationResult -> msg) -> Sub msg
port auth0logout : () -> Cmd msg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment