Skip to content

Commit 8e2e62d

Browse files
Merge pull request #39 from jacobweinstock/add-readme
Add readme
2 parents ef58ef2 + 086a578 commit 8e2e62d

2 files changed

Lines changed: 146 additions & 0 deletions

File tree

grpc/authz/README.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# gRPC Authorization
2+
3+
This package provides the ability to add authorization to a gRPC server. It is made to be coupled with github.com/grpc-ecosystem/go-grpc-middleware/auth.
4+
5+
## Background
6+
7+
Under the hood it is using [https://github.com/cristalhq/jwt](https://github.com/cristalhq/jwt) for all JWT functionality. The main reason this library was chosen was because [jwt.io](https://jwt.io/) reports it as supporting all [algorithms and validations](./images/cristalhq-jwt.io.png).
8+
9+
Currently, in this repo, the following algorithms are supported.
10+
11+
- HS256, HS384, HS512
12+
- RS256, RS384, RS512
13+
14+
## Usage
15+
16+
This example will validate that the JWT was signed by the given key and not expired.
17+
18+
```go
19+
package main
20+
21+
import (
22+
"net"
23+
"os"
24+
25+
jwt "github.com/cristalhq/jwt/v3"
26+
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
27+
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
28+
"github.com/packethost/pkg/grpc/authz"
29+
"google.golang.org/grpc"
30+
)
31+
32+
var (
33+
hsKey = []byte("supersecret")
34+
)
35+
36+
func main() {
37+
// create a Config
38+
// at a minimum an algorithm, scope mapping (only the methods defined here will protected), and key are needed
39+
config := authz.NewConfig(
40+
jwt.HS256,
41+
map[string][]string{
42+
"/github.com.tinkerbell.pbnj.api.v1.Machine/Power": {},
43+
},
44+
authz.WithHSKey(hsKey),
45+
46+
)
47+
48+
// the AuthFunc method can then be used with as middleware with a gRPC server
49+
grpcServer := grpc.NewServer(
50+
grpc_middleware.WithUnaryServerChain(
51+
grpc_auth.UnaryServerInterceptor(config.AuthFunc),
52+
),
53+
)
54+
55+
listen, err := net.Listen("tcp", ":50051")
56+
if err != nil {
57+
panic(err)
58+
}
59+
60+
if err := grpcServer.Serve(listen); err != nil {
61+
os.Exit(1)
62+
}
63+
}
64+
```
65+
66+
This example will validate that the JWT was signed by the given key, not expired, and custom scopes match the called method.
67+
68+
```go
69+
package main
70+
71+
import (
72+
"encoding/json"
73+
"net"
74+
"os"
75+
76+
jwt "github.com/cristalhq/jwt/v3"
77+
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
78+
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
79+
"github.com/packethost/pkg/grpc/authz"
80+
"google.golang.org/grpc"
81+
"google.golang.org/grpc/codes"
82+
"google.golang.org/grpc/status"
83+
)
84+
85+
var (
86+
hsKey = []byte("supersecret")
87+
)
88+
89+
func main() {
90+
// create a func for validating Scopes
91+
scopeFunc := func(tokenClaims []byte, scopes []string) error {
92+
type CustomClaims struct {
93+
jwt.StandardClaims
94+
Scopes []string `json:"scopes"`
95+
}
96+
var newClaims CustomClaims
97+
err := json.Unmarshal(tokenClaims, &newClaims)
98+
if err != nil {
99+
return status.Errorf(codes.Unauthenticated, "access token is invalid: %s", err.Error())
100+
}
101+
102+
if !contains(newClaims.Scopes, "write") {
103+
return status.Errorf(codes.PermissionDenied, "no permission to access this RPC: no matching scope found")
104+
}
105+
106+
return nil
107+
}
108+
// create a Config
109+
// at a minimum an algorithm, scope mapping (only the methods defined here will protected), and a key are needed. we set the scope validation
110+
// and audience on this one.
111+
config := authz.NewConfig(
112+
jwt.HS256,
113+
map[string][]string{
114+
"/github.com.tinkerbell.pbnj.api.v1.Machine/Power": {"write"},
115+
},
116+
authz.WithHSKey(hsKey),
117+
authz.WithValidateScopeFunc(scopeFunc),
118+
authz.WithAudience("admin"),
119+
)
120+
121+
// the AuthFunc method can then be used with as middleware with a gRPC server
122+
grpcServer := grpc.NewServer(
123+
grpc_middleware.WithUnaryServerChain(
124+
grpc_auth.UnaryServerInterceptor(config.AuthFunc),
125+
),
126+
)
127+
128+
listen, err := net.Listen("tcp", ":50051")
129+
if err != nil {
130+
panic(err)
131+
}
132+
133+
if err := grpcServer.Serve(listen); err != nil {
134+
os.Exit(1)
135+
}
136+
}
137+
138+
func contains(s []string, str string) bool {
139+
for _, v := range s {
140+
if v == str {
141+
return true
142+
}
143+
}
144+
return false
145+
}
146+
```
60.2 KB
Loading

0 commit comments

Comments
 (0)