Comments (7)
@sm3142, Hi! For now, it's not possible, but for you purpose, i can give you simple example how to make global authorization midleware, that handles only if it's needed by security restriction for each endpoint.
- Write you own huma router-agnostic middleware for security scheme reading and jwt parsing:
NewAuthMiddleware() {
return func(ctx huma.Context, next func(huma.Context)) {
var anyOfNeededRoles []string
isAuthorizationRequired := false
for _, opScheme := range ctx.Operation().Security {
var ok bool
if anyOfNeededRoles, ok = opScheme["Bearer"]; ok {
isAuthorizationRequired = true
}
}
// check isAuthorizationRequired flag,
// if true -
// parser jwt token from jwt header, get roles, check roles,
// if neded roles exist in jwt - handle next
// if no - write error to context with `huma.WriteErr` and not call next()
// if false - simply call next()
}
}
- Add Security Scheme and Middleware to huma
defconfig := huma.DefaultConfig("Some API V1", "1.0.0")
defconfig.Components.SecuritySchemes = map[string]*huma.SecurityScheme{
"Bearer": {
Type: "http",
Scheme: "bearer",
BearerFormat: "JWT",
},
}
api := humachi.New(chiMux, defconfig)|
api.UseMiddleware(NewAuthMiddleware())
- Add security to huma.Operation in huma.Register() func.
huma.Register(
api,
huma.Operation{
Summary: "Delete for admins only",
Method: http.MethodDelete,
Path: "/api/v2/admin/",
Security: []map[string][]string{
{"Bearer": {"admin"}},
},
Errors: []int{
http.StatusUnauthorized,
http.StatusForbidden,
http.StatusBadRequest,
},
},
func(ctx context.Context, input *request) (*struct{}, error) {
// this request will only be processed if the user is authorized and has the required roles
},
)
This is a very simple example, but I hope it helps you use the middlewares in Huma.
from huma.
@Insei @sm3142 I tried to put some of this info together into an initial how-to. It could use some feedback before I merge, please take a look at https://github.com/danielgtaylor/huma/pull/204/files?short_path=bb47c3b#diff-bb47c3bf4e16beb2e9b64f7d9a38cb4dd921384c2137d3586b856fb1cbc3dcd2
from huma.
@Insei thank you for the detailed explanation! Exactly what I was looking for. It somehow didn't occur to me to use the OpenAPI security spec in this way - likely because I was fixated on the router composition pattern exemplified by Chi (and others), trying to fit that particular square peg into the round hole.
If I may make a humble suggestion: I think the recipe above deserves its own how-to entry - I'm sure it would be helpful for others as well.
Anyway, thanks again for the great work with Huma!
from huma.
@sm3142 You are welcome! I think soon I will add a universal auth JWT middleware into huma, with the ability to choose access control by roles, claims or scopes, i already have a working solution, but its not ideal for now
from huma.
@JanRuettinger sorry for the delay I was dealing with covid and then catching up at work. Your two approaches are canonical and reasonable to me. What we wound up doing at work is having JWT decoding at the gateway and info like the user ID becomes a simple header which we take as an input parameter on the calls that need it. Other teams have directly taken the JWT at the service and used a resolver to parse it into its constituent fields for the audience, issuer, user, scopes/perms, etc.
As the original question was answered I'm going to close this, but feel free to open additional issues with more questions.
from huma.
With OAuth2: How to document the enpoints with jwt authentication, but the principle is the same =)
from huma.
I have a follow up question regarding the usual next step: retrieving the user object from the database.
The proposed solution makes sure that the operation handler is only executed if the request is authorized but how do we know what user triggered the request.
huma.Register(
api,
huma.Operation{
Summary: "Delete for admins only",
Method: http.MethodDelete,
Path: "/api/v2/admin/",
Security: []map[string][]string{
{"Bearer": {"admin"}},
},
Errors: []int{
http.StatusUnauthorized,
http.StatusForbidden,
http.StatusBadRequest,
},
},
func(ctx context.Context, input *request) (*struct{}, error) {
// this request will only be processed if the user is authorized and has the required roles
// GET USER FROM DB
},
)
I see two potential solutions:
-
Add the required information about a user, e.g. user_id, as an additional input parameter to the request. What I don't like about this approach is that it adds redundant information since the Bearer token already contains the user_id.
-
Use a resolver function to make the user available in the operation handler as shown here
Is there another approach which is more canonical?
from huma.
Related Issues (20)
- Support Discriminator and DiscriminatorMap HOT 2
- Setting headers in error response HOT 6
- Add more tests for cookie HOT 3
- Add custom header from SSE Handler HOT 3
- How should nullable query params be handled without pointers? HOT 4
- Add support for netip.Addr HOT 1
- Forwarding headers through the middleware not working HOT 5
- Override ErrorModel for single operations HOT 6
- Feature: Accept valueless path/query/header/cookie tags
- Docs UI doesn't handle multiple cookies properly HOT 1
- Allow custom Transform for nested objects HOT 2
- Error thrown when setting cli name HOT 2
- Error return for huma.StreamResponse.Body HOT 1
- Schema(r huma.Registry) *huma.Schema not working on Pointer
- Validation errors localization HOT 2
- Question: Is there a way to add comments to fields? HOT 1
- Version Chi v4 Into Separate Release
- Removing schema reference in response HOT 2
- Multiline doc description for struct fields
- Bug: API doc hidden is not *real* hidden HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from huma.