inter-hub-haskell/Web/Controller/ApiKeys.hs

54 lines
1.9 KiB
Haskell
Raw Normal View History

2026-04-01 19:52:20 +00:00
module Web.Controller.ApiKeys where
import Web.Types
import Web.View.ApiKeys.New
import Web.View.ApiKeys.Created
import Generated.Types
import IHP.Prelude
import IHP.ControllerPrelude
import qualified Data.Text.Encoding as TE
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Random as Random
instance Controller ApiKeysController where
beforeAction = ensureIsUser
action ApiKeysAction { apiConsumerId } = do
-- Redirect to consumer show page which displays keys
redirectTo (ShowApiConsumerAction apiConsumerId)
action NewApiKeyAction { apiConsumerId } = do
consumer <- fetch apiConsumerId
let apiKey = newRecord @ApiKey
render NewView { apiKey, consumer }
action CreateApiKeyAction = do
apiConsumerId <- param @(Id ApiConsumer) "apiConsumerId"
consumer <- fetch apiConsumerId
scopes <- fromMaybe "" <$> paramOrNothing @Text "scopes"
-- Generate a random 32-byte key, encode as hex (64 chars)
rawBytes <- liftIO $ Random.random 32
let fullKey = TE.decodeUtf8 (Base16.encode rawBytes)
let prefix = T.take 8 fullKey
let keyHash = TE.decodeUtf8 $ Base16.encode $ SHA256.hash (TE.encodeUtf8 fullKey)
_key <- newRecord @ApiKey
|> set #apiConsumerId consumer.id
|> set #keyPrefix prefix
|> set #keyHash keyHash
|> set #scopes scopes
|> set #tokenType "static"
|> createRecord
-- Show full key once; never again
render CreatedView { consumer, fullKey }
action RevokeApiKeyAction { apiKeyId } = do
apiKey <- fetch apiKeyId
now <- getCurrentTime
apiKey |> set #revokedAt (Just now) |> updateRecord
consumer <- fetch apiKey.apiConsumerId
redirectTo (ShowApiConsumerAction consumer.id)