{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module GitHub.REST.Auth
( Token(..)
, fromToken
, getJWTToken
, loadSigner
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid ((<>))
#endif
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import Data.Time (addUTCTime, getCurrentTime)
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
import qualified Web.JWT as JWT
data Token
= AccessToken ByteString
| BearerToken ByteString
deriving (Show)
fromToken :: Token -> ByteString
fromToken = \case
AccessToken t -> "token " <> t
BearerToken t -> "bearer " <> t
type AppId = Int
getJWTToken :: JWT.Signer -> AppId -> IO Token
getJWTToken signer appId = mkToken <$> getNow
where
#if MIN_VERSION_jwt(0,10,0)
signToken = flip JWT.encodeSigned mempty
#else
signToken = JWT.encodeSigned
#endif
mkToken now =
let claims = mempty
{ JWT.iat = JWT.numericDate $ utcTimeToPOSIXSeconds now
, JWT.exp = JWT.numericDate $ utcTimeToPOSIXSeconds now + (10 * 60)
, JWT.iss = JWT.stringOrURI $ Text.pack $ show appId
}
in BearerToken . Text.encodeUtf8 $ signToken signer claims
getNow = addUTCTime (-1) <$> getCurrentTime
loadSigner :: FilePath -> IO JWT.Signer
loadSigner file = maybe badSigner return . readSigner =<< ByteString.readFile file
where
badSigner = fail $ "Not a valid RSA private key file: " ++ file
readSigner = fmap JWT.RSAPrivateKey . JWT.readRsaSecret