IHP provides simple authorization helpers to restrict access to actions based on user authentication status and permissions.
Requiring Login
Use ensureIsUser to restrict an action to logged-in users:
action PostsAction = do
ensureIsUser
posts <- query @Post |> fetch
render IndexView { .. }
When an unauthenticated user tries to access a protected action, they’re redirected to the login page. After successful login, they’re redirected back to the original page.
Protecting All Actions in a Controller
Place ensureIsUser in the beforeAction hook to protect all actions:
instance Controller PostsController where
beforeAction = ensureIsUser
action PostsAction = do
posts <- query @Post |> fetch
render IndexView { .. }
action ShowPostAction { postId } = do
post <- fetch postId
render ShowView { .. }
Both PostsAction and ShowPostAction now require authentication.
Restricting to Admins
Use ensureIsAdmin to restrict actions to administrators:
instance Controller UserController where
beforeAction = ensureIsAdmin @Admin
If you get a type error about HasNewSessionUrl admin0, you may need to explicitly annotate the admin type with @Admin.
Custom Permissions
Use accessDeniedUnless to implement custom authorization logic:
action ShowPostAction { postId } = do
post <- fetch postId
accessDeniedUnless (post.userId == currentUserId)
render ShowView { .. }
This ensures users can only view their own posts.
Common Patterns
Resource Ownership
action EditPostAction { postId } = do
post <- fetch postId
accessDeniedUnless (post.userId == currentUserId)
render EditView { .. }
action UpdatePostAction { postId } = do
post <- fetch postId
accessDeniedUnless (post.userId == currentUserId)
post
|> buildPost
|> ifValid \case
Left post -> render EditView { .. }
Right post -> do
post <- post |> updateRecord
redirectTo ShowPostAction { .. }
Role-Based Access
action AdminDashboardAction = do
ensureIsUser
accessDeniedUnless (currentUser.role == "admin")
render DashboardView
Combining Conditions
action DeletePostAction { postId } = do
post <- fetch postId
let isOwner = post.userId == currentUserId
let isAdmin = currentUser.role == "admin"
accessDeniedUnless (isOwner || isAdmin)
deleteRecord post
redirectTo PostsAction