Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) Benito Gomez <benitogf@outlook.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ JWT authentication library for the [ooo](https://github.com/benitogf/ooo) ecosys

- **JWT token authentication** with configurable expiry
- **User management** with registration and login
- **Audit middleware** for access control
- **Gate middleware** for access control via `Router.Use()`
- **Compatible with ooo** server and filters

## Installation
Expand All @@ -24,23 +24,23 @@ package main

import (
"log"
"net/http"
"time"

"github.com/gorilla/mux"
"github.com/benitogf/auth"
"github.com/benitogf/ko"
"github.com/benitogf/ooo"
"github.com/benitogf/ooo/storage"
"github.com/gorilla/mux"
)

func main() {
// Auth storage (users)
authStore := &ko.Storage{Path: "/data/auth"}
err := authStore.Start([]string{}, nil)
if err != nil {
log.Fatal(err)
}
go ooo.WatchStorageNoop(authStore)
// Auth storage (users), persisted with ko
authStore := storage.New(storage.LayeredConfig{
Memory: storage.NewMemoryLayer(),
Embedded: ko.NewEmbeddedStorage("./auth_data"),
})
authStore.Start(storage.Options{})
go storage.WatchStorageNoop(authStore)

// Create auth with JWT token expiry
key := "your-secret-key"
Expand All @@ -51,19 +51,18 @@ func main() {

// Create server with static mode
app := ooo.Server{Static: true}
app.Router = mux.NewRouter()

// Audit middleware for access control
app.Audit = func(r *http.Request) bool {
if r.URL.Path == "/open" {
return true
}
return tokenAuth.Verify(r) // Require valid token
}
// Gate access with the auth middleware. ooo's Server.Audit hook was
// removed in favor of Router.Use(): the middleware fans out to every
// matched route, requiring a valid token for the data routes while
// leaving the auth-managed routes (register, authorize, ...) open.
// Pass extra open paths to exempt them.
app.Router.Use(tokenAuth.Middleware("/open"))

app.Router = mux.NewRouter()
app.OpenFilter("open") // Available without token
app.OpenFilter("closed") // Requires valid token
tokenAuth.Router(&app) // Add auth routes
tokenAuth.Routes(&app) // Add auth routes

app.Start("localhost:8800")
app.WaitClose()
Expand All @@ -74,9 +73,14 @@ func main() {

| Method | Path | Description |
|--------|------|-------------|
| POST | `/register` | Register new user |
| POST | `/authorize` | Login and get token |
| GET | `/verify` | Verify token validity |
| POST | `/register` | Register new user (open) |
| POST / PUT | `/authorize` | Login and get token / refresh an expired token |
| GET | `/available?account=` | Check if an account name is taken (open) |
| GET | `/profile` | Get the profile for the request token |
| POST | `/create` | Create a user (root/admin only) |
| GET | `/users` | List users (root/admin only) |
| GET / POST / DELETE | `/user/{account}` | Read, update or delete a user |
| PUT | `/password/{account}` | Update an account password |

## Related Projects

Expand Down
41 changes: 41 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,47 @@ func (t *TokenAuth) Verify(req *http.Request) bool {
return err == nil
}

// isAuthRoute reports whether path is one of the endpoints registered by
// Routes. Those handlers either are open (register, authorize, available) or
// self-guard by inspecting the token themselves (users, user, profile,
// password, create), so Middleware never gates them — mirroring the previous
// behavior where ooo's Server.Audit hook did not cover these custom handlers.
func isAuthRoute(path string) bool {
switch path {
case "/authorize", "/profile", "/users", "/register", "/create", "/available":
return true
}
return strings.HasPrefix(path, "/user/") || strings.HasPrefix(path, "/password/")
}

// Middleware returns a gorilla/mux middleware that gates requests with a valid
// token. It is the replacement for ooo's removed Server.Audit hook: register it
// before starting the server with
//
// server.Router.Use(tokenAuth.Middleware())
//
// gorilla/mux fans the middleware out to every matched route (REST, WebSocket
// upgrades, custom endpoints and the explorer UI), so any data route without a
// valid token is answered by UnauthorizedHandler. The auth-managed routes
// registered by Routes are never gated; pass extra open paths (exact match) to
// exempt additional routes.
func (t *TokenAuth) Middleware(open ...string) mux.MiddlewareFunc {
openSet := make(map[string]struct{}, len(open))
for _, path := range open {
openSet[path] = struct{}{}
}
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, isOpen := openSet[r.URL.Path]
if isOpen || isAuthRoute(r.URL.Path) || t.Verify(r) {
next.ServeHTTP(w, r)
return
}
t.UnauthorizedHandler(w, r)
})
}
}

// Authenticate :
func (t *TokenAuth) Authenticate(r *http.Request) (Token, error) {
strToken := t.getter.GetTokenFromRequest(r)
Expand Down
2 changes: 1 addition & 1 deletion auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ func TestRegisterAndAuthorize(t *testing.T) {
)
server := &ooo.Server{}
server.Silence = true
server.Audit = auth.Verify
server.Router = mux.NewRouter()
server.Router.Use(auth.Middleware())
auth.Routes(server)
server.Start("localhost:9060")
defer server.Close(os.Interrupt)
Expand Down
12 changes: 3 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/benitogf/auth
go 1.25

require (
github.com/benitogf/ooo v0.0.0-20260202060447-566ed1c50fb9
github.com/benitogf/ooo v0.0.0-20260728151819-8c38013e1823
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/gorilla/mux v1.8.1
github.com/stretchr/testify v1.11.1
Expand All @@ -13,20 +13,14 @@ require (
require (
github.com/bclicn/color v0.0.0-20180711051946-108f2023dc84 // indirect
github.com/benitogf/coat v0.0.0-20200402073050-ff807656cbec // indirect
github.com/benitogf/jsondiff v0.0.0-20220926080659-c3db9b84b559 // indirect
github.com/benitogf/jsonpatch v0.0.0-20260109052650-eec54232a9a2 // indirect
github.com/benitogf/go-json v0.0.0-20260410172501-727f5690408b // indirect
github.com/benitogf/jsonpatch v0.0.0-20260413094158-a4a6cc1a3382 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/kr/pretty v0.2.1 // indirect
github.com/pkg/expect v0.0.0-20191209053905-1fe4c9394a8a // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
21 changes: 8 additions & 13 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ github.com/bclicn/color v0.0.0-20180711051946-108f2023dc84 h1:cutFptzj+ospnc1PET
github.com/bclicn/color v0.0.0-20180711051946-108f2023dc84/go.mod h1:Va9ap1qxjAWkIVaW1E9rH0aNgE8SDI5A4n8Ds8P0fAA=
github.com/benitogf/coat v0.0.0-20200402073050-ff807656cbec h1:I2p/9YsiAMB+J2DrC+e7OvEWZCmpwolMQ23ejoEMFEE=
github.com/benitogf/coat v0.0.0-20200402073050-ff807656cbec/go.mod h1:hl/Xd8DKuMgC/ClZ2dPZz6mvXaxdkjrt0mr0+jF4UkQ=
github.com/benitogf/jsondiff v0.0.0-20220926080659-c3db9b84b559 h1:0ERndS9JkEvEq/yXjiZq41lMb3QfmQWUIXycjkRbWOc=
github.com/benitogf/jsondiff v0.0.0-20220926080659-c3db9b84b559/go.mod h1:gg2qmcNCGq/FXh5kQpsaEIf6GebYe2JMKrDSqZkuImc=
github.com/benitogf/jsonpatch v0.0.0-20260109052650-eec54232a9a2 h1:OC5ZiOG0sQ5Qmp5XOA3iIBBjhDjze010jHicNTD27kU=
github.com/benitogf/jsonpatch v0.0.0-20260109052650-eec54232a9a2/go.mod h1:O1Z+hTPUrXzHUjmKlpmNYK9wcncZ5rS3K8PqItDO0x0=
github.com/benitogf/ooo v0.0.0-20260109055348-efad8e781ed7 h1:qNmJ0qTWXW8k8XYLMfXy4cEYEA2VwMTL6P+GUFbdhHo=
github.com/benitogf/ooo v0.0.0-20260109055348-efad8e781ed7/go.mod h1:fqf1bDkBBOOiCExWW1qdj59ldb/yqfJc3gTfskl02cU=
github.com/benitogf/ooo v0.0.0-20260202060447-566ed1c50fb9 h1:P4TAfI7Hr8NzOuHqnwkO21lef3yaMG0TEo0M+F0ue4Y=
github.com/benitogf/ooo v0.0.0-20260202060447-566ed1c50fb9/go.mod h1:fqf1bDkBBOOiCExWW1qdj59ldb/yqfJc3gTfskl02cU=
github.com/benitogf/go-json v0.0.0-20260410172501-727f5690408b h1:EXDsF3gMYyR8ipJsGuyHB4d3+XovPT8d4UM6cfBII14=
github.com/benitogf/go-json v0.0.0-20260410172501-727f5690408b/go.mod h1:bv78ZxbWzHLAwEcSgQNjDUVgl3F4nOuYln98xHfUyjw=
github.com/benitogf/jsondiff v0.0.0-20260413094925-a4be838c278b h1:u7JAhkxSL5nMmH0HbpIg9w6qs7CYpdXiOiYemC3Zodo=
github.com/benitogf/jsondiff v0.0.0-20260413094925-a4be838c278b/go.mod h1:u6UwX3TEBaFuJVD3NuCdMp3RyPsb71DxRdMptVDwTaA=
github.com/benitogf/jsonpatch v0.0.0-20260413094158-a4a6cc1a3382 h1:invi4tSUoH4HMxFbM65sA3osc0wk9j3Za2JmtIqFRJc=
github.com/benitogf/jsonpatch v0.0.0-20260413094158-a4a6cc1a3382/go.mod h1:ZOQm93UFJwYDZLHQVGt2/JmNOltb6LRIJUuys0uMWKY=
github.com/benitogf/ooo v0.0.0-20260728151819-8c38013e1823 h1:n1UJP0mPGK0XwTJAPwqIQ7RteRKEuM09paZ5E7bdkLw=
github.com/benitogf/ooo v0.0.0-20260728151819-8c38013e1823/go.mod h1:WvPwWgfK2mo3UKfR+BM/9hwHe+ZjVwZZ3rxOPcBcXnw=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
Expand All @@ -18,8 +18,6 @@ github.com/getlantern/httptest v0.0.0-20161025015934-4b40f4c7e590 h1:OhyiFx+yBN3
github.com/getlantern/httptest v0.0.0-20161025015934-4b40f4c7e590/go.mod h1:rE/jidqqHHG9sjSxC24Gd5YCfZ1AT91C2wjJ28TAOfA=
github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848 h1:2MhMMVBTnaHrst6HyWFDhwQCaJ05PZuOv1bE2gN8WFY=
github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848/go.mod h1:+F5GJ7qGpQ03DBtcOEyQpM30ix4BLswdaojecFtsdy8=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
Expand All @@ -42,13 +40,10 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
Expand Down
Loading