Last active
April 13, 2017 14:00
-
-
Save kspeakman/bd13d5b922a6abfcbb480a907161030c to your computer and use it in GitHub Desktop.
JWT expiration notification
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Try to setup auto logout. But JWT parsing can fail in several ways. | |
-- | |
-- Failure strategy: | |
-- 1. Console log failure. | |
-- 2. The fallback (not shown below) is to instruct the user to | |
-- manually log out and back in when we receive 401 Unauthorized. | |
-- | |
-- TODO send failure to logging infrastructure. | |
notifyOnExpired : String -> Cmd Msg | |
notifyOnExpired token = | |
token | |
|> Jwt.onExpired Logout | |
|> Result.mapError (Debug.log "Auto-Logout could not be setup") | |
|> Result.withDefault Cmd.none |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Jwt exposing (JwtError(..), getExpiration, onExpired) | |
import String | |
import Base64 | |
import Json.Decode as Json | |
import Time exposing (Time) | |
import Result | |
import Process | |
import Task | |
-- https://github.com/simonh1000/elm-jwt | |
-- used as a starter for this code | |
type JwtError | |
= InvalidJwtStructure | |
-- 3 sections required | |
| InvalidEncodingLength | |
-- only 1 or 2 padding characters are allowed in base 64 | |
| Base64DecodingFailed String | |
| JsonDecodingFailed String | |
unurl : String -> String | |
unurl = | |
let | |
fix c = | |
case c of | |
'-' -> | |
'+' | |
'_' -> | |
'/' | |
c -> | |
c | |
in | |
String.map fix | |
fixlength : String -> Result JwtError String | |
fixlength s = | |
case String.length s % 4 of | |
0 -> | |
Result.Ok s | |
3 -> | |
Result.Ok <| String.concat [ s, "=" ] | |
2 -> | |
Result.Ok <| String.concat [ s, "==" ] | |
_ -> | |
Result.Err InvalidEncodingLength | |
expDecoder : Json.Decoder Float | |
expDecoder = | |
Json.field "exp" Json.float | |
getPayload : String -> Result JwtError String | |
getPayload token = | |
case token |> String.split "." of | |
header :: payload :: signature :: [] -> | |
payload | |
|> unurl | |
|> fixlength | |
_ -> | |
Result.Err InvalidJwtStructure | |
decodeBase64 : String -> Result JwtError String | |
decodeBase64 = | |
Base64.decode | |
>> Result.mapError Base64DecodingFailed | |
decodeExp : String -> Result JwtError Float | |
decodeExp = | |
Json.decodeString expDecoder | |
>> Result.mapError JsonDecodingFailed | |
-- TODO put somewhere else | |
(>>=) : Result x a -> (a -> Result x b) -> Result x b | |
(>>=) x f = | |
Result.andThen f x | |
-- TODO put somewhere else | |
(<$>) : Result x a -> (a -> b) -> Result x b | |
(<$>) x f = | |
Result.map f x | |
timeUntil : Time -> Time -> Time | |
timeUntil expiresAt now = | |
expiresAt - now | |
withMin : Time -> Time -> Time | |
withMin minTime expiresIn = | |
if expiresIn < minTime then | |
minTime | |
else | |
expiresIn | |
sendMsgAt : msg -> Time -> Cmd msg | |
sendMsgAt msg expiresAt = | |
Time.now | |
|> Task.map (timeUntil expiresAt >> withMin (1 * Time.second)) | |
|> Task.andThen Process.sleep | |
|> Task.perform (always msg) | |
getExpiration : String -> Result JwtError Time | |
getExpiration token = | |
getPayload token | |
>>= decodeBase64 | |
>>= decodeExp | |
<$> (*) Time.second | |
onExpired : msg -> String -> Result JwtError (Cmd msg) | |
onExpired msg token = | |
getExpiration token | |
<$> sendMsgAt msg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* add this dependency to elm-package.json */ | |
{ | |
"dependencies": { | |
"truqu/elm-base64": "1.0.0 <= v < 2.0.0" | |
}, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment