Skip to content

Commit ecdcaf3

Browse files
committed
Update uses of defaultJWTSettings in Share and example
1 parent 3714f1b commit ecdcaf3

File tree

2 files changed

+40
-25
lines changed

2 files changed

+40
-25
lines changed

app/Env.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ withEnv action = do
5252
githubClientID <- fromEnv "SHARE_GITHUB_CLIENTID" (pure . Right . Text.pack)
5353
githubClientSecret <- fromEnv "SHARE_GITHUB_CLIENT_SECRET" (pure . Right . Text.pack)
5454
hs256Key <- fromEnv "SHARE_HMAC_KEY" (pure . Right . BS.pack)
55+
edDSAKey <- fromEnv "SHARE_EDDSA_KEY" (pure . Right . BS.pack)
5556
zendeskAPIUser <- fromEnv "SHARE_ZENDESK_API_USER" (pure . Right . BS.pack)
5657
zendeskAPIToken <- fromEnv "SHARE_ZENDESK_API_TOKEN" (pure . Right . BS.pack)
5758
let zendeskAuth = Servant.BasicAuthData zendeskAPIUser zendeskAPIToken
@@ -88,7 +89,12 @@ withEnv action = do
8889
| otherwise = Nothing
8990
in r {Redis.connectTLSParams = tlsParams}
9091
let acceptedAudiences = Set.singleton apiOrigin
91-
let jwtSettings = JWT.defaultJWTSettings hs256Key acceptedAudiences apiOrigin
92+
let legacyKey = JWT.KeyDescription {JWT.key = hs256Key, JWT.alg = JWT.HS256}
93+
let signingKey = JWT.KeyDescription {JWT.key = edDSAKey, JWT.alg = JWT.Ed25519}
94+
let rotatedKeys = Set.empty
95+
jwtSettings <- case JWT.defaultJWTSettings signingKey (Just legacyKey) rotatedKeys acceptedAudiences apiOrigin of
96+
Left cryptoError -> throwIO cryptoError
97+
Right settings -> pure settings
9298
let cookieSettings = Cookies.defaultCookieSettings Deployment.onLocal (Just (realToFrac cookieSessionTTL))
9399
let sessionCookieKey = tShow Deployment.deployment <> "-share-session"
94100
redisConnection <- Redis.checkedConnect redisConfig

share-auth/example/src/Lib.hs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import Data.Set qualified as Set
88
import Data.Text (Text)
99
import Data.Time (DiffTime)
1010
import Database.Redis qualified as R
11+
import GHC.Stack (HasCallStack)
12+
import Network.URI qualified as URI
13+
import Network.Wai.Handler.Warp qualified as Warp
14+
import Servant
1115
import Share.JWT qualified as JWT
1216
import Share.OAuth.API (ServiceProviderAPI)
1317
import Share.OAuth.IdentityProvider.Share qualified as Share
@@ -18,10 +22,6 @@ import Share.OAuth.ServiceProvider qualified as Auth
1822
import Share.OAuth.Session (AuthCheckCtx, AuthenticatedUserId, MaybeAuthenticatedUserId, addAuthCheckCtx)
1923
import Share.OAuth.Types (OAuthClientId (..), OAuthClientSecret (OAuthClientSecret), RedirectReceiverErr, UserId)
2024
import Share.Utils.Servant.Cookies qualified as Cookies
21-
import GHC.Stack (HasCallStack)
22-
import Network.URI qualified as URI
23-
import Network.Wai.Handler.Warp qualified as Warp
24-
import Servant
2525
import UnliftIO
2626

2727
-- | An example application endpoint which is optionally authenticated.
@@ -44,30 +44,30 @@ type MyAPI =
4444
:<|> "error" :> ErrorEndpoint
4545

4646
-- | A handler which checks if the user is authenticated.
47-
mayAuthedEndpoint :: MonadIO m => Maybe UserId -> m String
47+
mayAuthedEndpoint :: (MonadIO m) => Maybe UserId -> m String
4848
mayAuthedEndpoint mayCallerUserId = do
4949
case mayCallerUserId of
5050
Nothing -> pure "no user"
5151
Just userId -> do
5252
pure $ "Hello, " <> show userId
5353

5454
-- | A handler which requires an authenticated user.
55-
authedEndpoint :: MonadIO m => UserId -> m String
55+
authedEndpoint :: (MonadIO m) => UserId -> m String
5656
authedEndpoint callerUserId = do
5757
pure $ "Hello, " <> show callerUserId
5858

5959
-- | A handler which displays errors from the OAuth2 flow.
60-
errorEndpoint :: Applicative m => Maybe String -> m String
60+
errorEndpoint :: (Applicative m) => Maybe String -> m String
6161
errorEndpoint err = do
6262
pure $ fromMaybe "no error" err
6363

6464
-- | A helper function for constructing URIs from constant strings.
65-
unsafeURI :: HasCallStack => String -> URI.URI
65+
unsafeURI :: (HasCallStack) => String -> URI.URI
6666
unsafeURI = fromJust . URI.parseURI
6767

6868
-- | A session callback which redirects the user to either an error page
6969
-- or the authed handler endpoint depending on whether the oauth2 login succeeds.
70-
mySessionCallback :: Applicative m => Either RedirectReceiverErr SessionCallbackData -> m URI
70+
mySessionCallback :: (Applicative m) => Either RedirectReceiverErr SessionCallbackData -> m URI
7171
mySessionCallback (Left err) = pure . fromJust . URI.parseURI $ "http://cloud:3030/error?error=" <> show err
7272
mySessionCallback (Right _session) = pure $ unsafeURI "http://cloud:3030/authed"
7373

@@ -78,7 +78,12 @@ main = do
7878
redisConn <- R.checkedConnect R.defaultConnectInfo
7979
putStrLn "booting up"
8080

81-
Warp.run 3030 $ serveWithContext (Proxy @MyAPI) ctx (myServer redisConn)
81+
jwtSettings <- case JWT.defaultJWTSettings signingKey (Just legacyKey) rotatedKeys acceptedAudiences issuer of
82+
Left cryptoError -> throwIO cryptoError
83+
Right jwtS -> do
84+
pure jwtS
85+
86+
Warp.run 3030 $ serveWithContext (Proxy @MyAPI) (ctx jwtSettings) (myServer redisConn jwtSettings)
8287
putStrLn "exiting"
8388
pure ()
8489
where
@@ -87,18 +92,18 @@ main = do
8792
apiProxy :: Proxy MyAPI
8893
apiProxy = Proxy
8994
-- The api context required by servant-auth
90-
appCtx :: (Context '[Cookies.CookieSettings, JWT.JWTSettings])
91-
appCtx = cookieSettings :. jwtSettings :. EmptyContext
95+
appCtx :: JWT.JWTSettings -> (Context '[Cookies.CookieSettings, JWT.JWTSettings])
96+
appCtx jwtSettings = cookieSettings :. jwtSettings :. EmptyContext
9297
sessionCookieKey :: Text
9398
sessionCookieKey = "session"
94-
ctx :: Context (AuthCheckCtx .++ '[Cookies.CookieSettings, JWT.JWTSettings])
95-
ctx = addAuthCheckCtx cookieSettings jwtSettings "session" appCtx
96-
serviceProviderEndpoints :: ServerT ServiceProviderAPI R.Redis
97-
serviceProviderEndpoints = Auth.serviceProviderServer Share.localShareIdentityProvider spConfig mySessionCallback
98-
myServer :: R.Connection -> Server MyAPI
99-
myServer conn =
99+
ctx :: JWT.JWTSettings -> Context (AuthCheckCtx .++ '[Cookies.CookieSettings, JWT.JWTSettings])
100+
ctx jwtSettings = addAuthCheckCtx cookieSettings jwtSettings "session" (appCtx jwtSettings)
101+
serviceProviderEndpoints :: JWT.JWTSettings -> ServerT ServiceProviderAPI R.Redis
102+
serviceProviderEndpoints jwtSettings = Auth.serviceProviderServer Share.localShareIdentityProvider (spConfig jwtSettings) mySessionCallback
103+
myServer :: R.Connection -> JWT.JWTSettings -> Server MyAPI
104+
myServer conn jwtSettings =
100105
Servant.hoistServerWithContext apiProxy ctxProxy (unRedis conn) $
101-
serviceProviderEndpoints
106+
serviceProviderEndpoints jwtSettings
102107
:<|> mayAuthedEndpoint
103108
:<|> authedEndpoint
104109
:<|> errorEndpoint
@@ -108,10 +113,8 @@ main = do
108113
cookieDefaultTTL = Just $ 60 * 60 * 24 * 7 -- 1 week
109114
cookieSettings :: Cookies.CookieSettings
110115
cookieSettings = Cookies.defaultCookieSettings onLocal cookieDefaultTTL
111-
jwtSettings :: JWT.JWTSettings
112-
jwtSettings = JWT.defaultJWTSettings hs256Key acceptedAudiences issuer
113-
spConfig :: ServiceProviderConfig
114-
spConfig =
116+
spConfig :: JWT.JWTSettings -> ServiceProviderConfig
117+
spConfig jwtSettings =
115118
ServiceProviderConfig
116119
{ cookieSettings,
117120
jwtSettings = jwtSettings,
@@ -124,7 +127,13 @@ main = do
124127
sessionCookieKey
125128
}
126129
onLocal = True
127-
hs256Key = "gpeakbroleymbscyqzrcalpemrjayhur"
130+
-- Ensure you use cryptographically secure 32-byte keys in production use.
131+
-- And don't re-use keys.
132+
hs256Key = "example-32-byte-hs256Key-jayhuxr"
133+
edDSAKey = "example-32-byte-edDSAKey-dxencne"
134+
legacyKey = JWT.KeyDescription {JWT.key = hs256Key, JWT.alg = JWT.HS256}
135+
signingKey = JWT.KeyDescription {JWT.key = edDSAKey, JWT.alg = JWT.Ed25519}
136+
rotatedKeys = Set.empty
128137
api = unsafeURI "http://cloud:3030"
129138
serviceAudience = api
130139
acceptedAudiences = Set.singleton serviceAudience

0 commit comments

Comments
 (0)