Skip to main content
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

Build docs developers (and LLMs) love