| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
parent directory.. | ||||
This little server is an example demonstrating how JWT's can be handled somewhat automatically with per-path validation of scopes.
We use similar code in production at DeepMap, and this example shows how authenticated API's can be written, as well as unit tested.
Some parts of this code can be generalized in the future, but for now, feel free to copy/paste and modify this example code.
We define a trivial server which allows for creating and listing Things, please see api.yaml.
In #components/securitySchemes we define a scheme named BearerAuth:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWTThis security scheme is a global requirement for all API endpoints:
security:
- BearerAuth: [ ]
This means that all API endpoints require a JWT bearer token for access, and without any specific scopes, as denoted by [].
However, we want our addThing operation to require a special write permission, denoted by things:w. This is a convention that we use in naming scopes, noun followed by type of access, in this case, :w means write. Read permission is implicit from having a valid JWT.
To specialize the addThing handler, we override the global security with local security on POST /things:
security:
- BearerAuth:
- "things:w"Security is tricky, so we need to leverage well tested libraries for doing validation instead of implementing too much ourselves. We've chosen to use the excellent github.com/lestrrat-go/jwx library for JWT validation, and the kin-openapi request filter to help us perform validation.
First, we need to configure our OapiRequestValidator to perform authentication:
validator := middleware.OapiRequestValidatorWithOptions(spec,
&middleware.Options{
Options: openapi3filter.Options{
AuthenticationFunc: NewAuthenticator(v),
},
})Whenever a request comes in, openapi3filter will call the AuthenticationFunc to tell it whether the request is valid. Please see the Authenticate function in server/jwt_authenticator.go for an example how to do this.
In this example, we set up several components:
You can run the example Echo server like so:
$ go run ./examples/authenticated-api/echo/main.go
2021/10/07 14:32:45 Reader token eyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOltdfQ.Hf9dCNJLa0HQfbtJi7ndASbkTfrLc6bZBJK8HaPqtzXiDkTH6sMRoiNhf6Kb1g6z3R1tN3XEpXsghxlMRO3OLA
2021/10/07 14:32:45 Writer token eyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOlsidGhpbmdzOnciXX0.CbPT1hzWmyTt0lTyv-fiyUlnY1SGa0vrX52yFjeigx2PA1-78LVH0z5hukPKkLMPDMXL9AJrtNp0elWSD_qrBw
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.2.1
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
It prints out two tokens for you to use in http requests. Let's assign these to some environment variables for convenience.
export RJWT=eyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOltdfQ.Hf9dCNJLa0HQfbtJi7ndASbkTfrLc6bZBJK8HaPqtzXiDkTH6sMRoiNhf6Kb1g6z3R1tN3XEpXsghxlMRO3OLA
export WJWT=eyJhbGciOiJFUzI1NiIsImtpZCI6ImZha2Uta2V5LWlkIiwidHlwIjoiSldUIn0.eyJhdWQiOlsiZXhhbXBsZS11c2VycyJdLCJpc3MiOiJmYWtlLWlzc3VlciIsInBlcm0iOlsidGhpbmdzOnciXX0.CbPT1hzWmyTt0lTyv-fiyUlnY1SGa0vrX52yFjeigx2PA1-78LVH0z5hukPKkLMPDMXL9AJrtNp0elWSD_qrBwLet's see how this works in practice. My example commands are using HTTPie, which I find easier to use in a shell than curl:
Unauthenticated requests fail:
$ http http://localhost:8080/things
HTTP/1.1 403 Forbidden
Content-Length: 43
Content-Type: application/json; charset=UTF-8
Date: Thu, 07 Oct 2021 22:00:32 GMT
{
"message": "Security requirements failed"
}
$ http POST http://localhost:8080/things name=SomeThing
HTTP/1.1 403 Forbidden
Content-Length: 43
Content-Type: application/json; charset=UTF-8
Date: Thu, 07 Oct 2021 22:01:11 GMT
{
"message": "Security requirements failed"
}
Using the Writer JWT, we can insert a Thing into the server:
$ http POST http://localhost:8080/things name=SomeThing Authorization:"Bearer $WJWT"
HTTP/1.1 201 Created
Content-Length: 28
Content-Type: application/json; charset=UTF-8
Date: Thu, 07 Oct 2021 22:02:05 GMT
{
"id": 0,
"name": "SomeThing"
}
However, we can not insert a Thing using the reader JWT:
$ http POST http://localhost:8080/things name=SomeThing2 Authorization:"Bearer $RJWT"
HTTP/1.1 403 Forbidden
Content-Length: 43
Content-Type: application/json; charset=UTF-8
Date: Thu, 07 Oct 2021 22:02:39 GMT
{
"message": "Security requirements failed"
}
Both JWT's, however, permit listing Things:
$ http http://localhost:8080/things Authorization:"Bearer $RJWT"
HTTP/1.1 200 OK
Content-Length: 30
Content-Type: application/json; charset=UTF-8
Date: Thu, 07 Oct 2021 22:03:12 GMT
[
{
"id": 0,
"name": "SomeThing"
}
]
$ http http://localhost:8080/things Authorization:"Bearer $WJWT"
HTTP/1.1 200 OK
Content-Length: 30
Content-Type: application/json; charset=UTF-8
Date: Thu, 07 Oct 2021 22:03:34 GMT
[
{
"id": 0,
"name": "SomeThing"
}
]
| Back | FazBrowse Home | New Git URL |