honojs-middleware/packages/casbin/README.md

141 lines
3.9 KiB
Markdown

# Casbin Middleware for Hono
This is a third-party [Casbin](https://casbin.org) middleware for [Hono](https://github.com/honojs/hono).
This middleware can be used to enforce authorization policies defined using Casbin in your Hono routes.
## Installation
```bash
npm i hono @hono/casbin casbin
```
## Configuration
Before using the middleware, you must set up your Casbin model and policy files.
For details on how to write authorization policies and other information, please refer to the [Casbin documentation](https://casbin.org/).
### Example model.conf
```conf
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
```
### Example policy.csv
```csv
p, alice, /dataset1/*, *
p, bob, /dataset1/*, GET
```
## Usage with Basic HTTP Authentication
You can perform authorization control after Basic authentication by combining it with `basicAuthorizer`.
(The client needs to send `Authentication: Basic {Base64Encoded(username:password)}`.)
Let's look at an example.
Use the `model` and `policy` files from the [Configuration](#configuration) section.
You can implement a scenario where `alice` and `bob` have different permissions. Alice has access to all methods on `/dataset1/test`, while Bob has access only to the `GET` method.
```ts
import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'
import { newEnforcer } from 'casbin'
import { casbin } from '@hono/casbin'
import { basicAuthorizer } from '@hono/casbin/helper'
const app = new Hono()
app.use('*',
basicAuth(
{
username: 'alice', // alice has full access to /dataset1/test
password: 'password',
},
{
username: 'bob', // bob cannot post to /dataset1/test
password: 'password',
}
),
casbin({
newEnforcer: newEnforcer('examples/model.conf', 'examples/policy.csv'),
authorizer: basicAuthorizer
})
)
app.get('/dataset1/test', (c) => c.text('dataset1 test')) // alice and bob can access /dataset1/test
app.post('/dataset1/test', (c) => c.text('dataset1 test')) // Only alice can access /dataset1/test
```
## Usage with JWT Authentication
By using `jwtAuthorizer`, you can perform authorization control after JWT authentication.
By default, `jwtAuthorizer` uses the `sub` in the JWT payload as the username.
```ts
import { Hono } from 'hono'
import { jwt } from 'hono/jwt'
import { newEnforcer } from 'casbin'
import { casbin } from '@hono/casbin'
import { jwtAuthorizer } from '@hono/casbin/helper'
const app = new Hono()
app.use('*',
jwt({
secret: 'it-is-very-secret',
}),
casbin({
newEnforcer: newEnforcer('examples/model.conf', 'examples/policy.csv'),
authorizer: jwtAuthorizer
})
)
app.get('/dataset1/test', (c) => c.text('dataset1 test')) // alice and bob can access /dataset1/test
app.post('/dataset1/test', (c) => c.text('dataset1 test')) // Only alice can access /dataset1/test
```
Of course, you can use claims other than the `sub` claim.
Specify the `key` as a user-friendly name and the `value` as the JWT claim name. The `Payload` key used for evaluation in the enforcer will be the `value`.
```ts
const claimMapping = {
username: 'username',
}
// ...
casbin({
newEnforcer: newEnforcer('examples/model.conf', 'examples/policy.csv'),
authorizer: (c, e) => jwtAuthorizer(c, e, claimMapping)
})
```
## Usage with Customized Authorizer
You can also use a customized authorizer function to handle the authorization logic.
```ts
import { Hono } from 'hono'
import { newEnforcer } from 'casbin'
import { casbin } from '@hono/casbin'
const app = new Hono()
app.use('*', casbin({
newEnforcer: newEnforcer('path-to-your-model.conf', 'path-to-your-policy.csv'),
authorizer: async (c, enforcer) => {
const { user, path, method } = c
return await enforcer.enforce(user, path, method)
}
}))
```
## Author
sugar-cat https://github.com/sugar-cat7