Skip to content

Commit 44d9dbe

Browse files
authored
Merge pull request #41 from auth0-samples/patch/restructure
[SDK-2848] Reorganize code into packages
2 parents c1755d0 + 14545af commit 44d9dbe

3 files changed

Lines changed: 182 additions & 158 deletions

File tree

01-Authorization-RS256/main.go

Lines changed: 7 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -1,182 +1,31 @@
11
package main
22

33
import (
4-
"encoding/json"
5-
"errors"
6-
"fmt"
74
"log"
85
"net/http"
9-
"os"
10-
"strings"
116

12-
jwtmiddleware "github.com/auth0/go-jwt-middleware"
13-
"github.com/codegangsta/negroni"
14-
"github.com/form3tech-oss/jwt-go"
15-
"github.com/gorilla/mux"
167
"github.com/joho/godotenv"
178
"github.com/rs/cors"
18-
)
19-
20-
type Response struct {
21-
Message string `json:"message"`
22-
}
23-
24-
type Jwks struct {
25-
Keys []JSONWebKeys `json:"keys"`
26-
}
279

28-
type JSONWebKeys struct {
29-
Kty string `json:"kty"`
30-
Kid string `json:"kid"`
31-
Use string `json:"use"`
32-
N string `json:"n"`
33-
E string `json:"e"`
34-
X5c []string `json:"x5c"`
35-
}
10+
"01-Authorization-RS256/router"
11+
)
3612

3713
func main() {
38-
err := godotenv.Load()
39-
if err != nil {
14+
if err := godotenv.Load(); err != nil {
4015
log.Print("Error loading .env file")
4116
}
4217

43-
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
44-
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
45-
// Verify 'aud' claim
46-
aud := os.Getenv("AUTH0_AUDIENCE")
47-
checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
48-
if !checkAud {
49-
return token, errors.New("Invalid audience.")
50-
}
51-
// Verify 'iss' claim
52-
iss := "https://" + os.Getenv("AUTH0_DOMAIN") + "/"
53-
checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
54-
if !checkIss {
55-
return token, errors.New("Invalid issuer.")
56-
}
57-
58-
cert, err := getPemCert(token)
59-
if err != nil {
60-
panic(err.Error())
61-
}
62-
63-
result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
64-
return result, nil
65-
},
66-
SigningMethod: jwt.SigningMethodRS256,
67-
})
68-
6918
c := cors.New(cors.Options{
7019
AllowedOrigins: []string{"http://localhost:3000"},
7120
AllowCredentials: true,
7221
AllowedHeaders: []string{"Authorization"},
7322
})
7423

75-
r := mux.NewRouter()
76-
77-
// This route is always accessible
78-
r.Handle("/api/public", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
79-
message := "Hello from a public endpoint! You don't need to be authenticated to see this."
80-
responseJSON(message, w, http.StatusOK)
81-
}))
82-
83-
// This route is only accessible if the user has a valid access_token
84-
// We are chaining the jwtmiddleware middleware into the negroni handler function which will check
85-
// for a valid token.
86-
r.Handle("/api/private", negroni.New(
87-
negroni.HandlerFunc(jwtMiddleware.HandlerWithNext),
88-
negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
89-
message := "Hello from a private endpoint! You need to be authenticated to see this."
90-
responseJSON(message, w, http.StatusOK)
91-
}))))
92-
93-
// This route is only accessible if the user has a valid access_token with the read:messages scope
94-
// We are chaining the jwtmiddleware middleware into the negroni handler function which will check
95-
// for a valid token and scope.
96-
r.Handle("/api/private-scoped", negroni.New(
97-
negroni.HandlerFunc(jwtMiddleware.HandlerWithNext),
98-
negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
99-
token := r.Context().Value("user").(*jwt.Token)
100-
101-
hasScope := checkScope("read:messages", token)
102-
if !hasScope {
103-
message := "Insufficient scope."
104-
responseJSON(message, w, http.StatusForbidden)
105-
return
106-
}
107-
108-
message := "Hello from a private endpoint! You need to be authenticated to see this."
109-
responseJSON(message, w, http.StatusOK)
110-
}))))
111-
24+
r := router.New()
11225
handler := c.Handler(r)
113-
http.Handle("/", r)
114-
fmt.Println("Listening on http://localhost:3010")
115-
http.ListenAndServe("0.0.0.0:3010", handler)
116-
}
117-
118-
func checkScope(scope string, token *jwt.Token) bool {
119-
claims, ok := token.Claims.(jwt.MapClaims)
120-
if !ok {
121-
return false
122-
}
12326

124-
const scopeKey = "scope"
125-
tokenScope, ok := claims[scopeKey].(string)
126-
if !ok {
127-
return false
27+
log.Print("Server listening on http://localhost:3010")
28+
if err := http.ListenAndServe("0.0.0.0:3010", handler); err != nil {
29+
log.Fatalf("There was an error with the http server: %v", err)
12830
}
129-
130-
result := strings.Split(tokenScope, " ")
131-
for i := range result {
132-
if result[i] == scope {
133-
return true
134-
}
135-
}
136-
137-
return false
138-
}
139-
140-
func getPemCert(token *jwt.Token) (string, error) {
141-
cert := ""
142-
resp, err := http.Get("https://" + os.Getenv("AUTH0_DOMAIN") + "/.well-known/jwks.json")
143-
144-
if err != nil {
145-
return cert, err
146-
}
147-
defer resp.Body.Close()
148-
149-
var jwks = Jwks{}
150-
err = json.NewDecoder(resp.Body).Decode(&jwks)
151-
152-
if err != nil {
153-
return cert, err
154-
}
155-
156-
for k, _ := range jwks.Keys {
157-
if token.Header["kid"] == jwks.Keys[k].Kid {
158-
cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----"
159-
}
160-
}
161-
162-
if cert == "" {
163-
err := errors.New("Unable to find appropriate key.")
164-
return cert, err
165-
}
166-
167-
return cert, nil
168-
}
169-
170-
func responseJSON(message string, w http.ResponseWriter, statusCode int) {
171-
response := Response{message}
172-
173-
jsonResponse, err := json.Marshal(response)
174-
if err != nil {
175-
http.Error(w, err.Error(), http.StatusInternalServerError)
176-
return
177-
}
178-
179-
w.Header().Set("Content-Type", "application/json")
180-
w.WriteHeader(statusCode)
181-
w.Write(jsonResponse)
18231
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package middleware
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"net/http"
7+
"os"
8+
9+
"github.com/auth0/go-jwt-middleware"
10+
"github.com/form3tech-oss/jwt-go"
11+
)
12+
13+
var JWT = jwtmiddleware.New(jwtmiddleware.Options{
14+
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
15+
// Verify 'aud' claim
16+
aud := os.Getenv("AUTH0_AUDIENCE")
17+
checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
18+
if !checkAud {
19+
return token, errors.New("invalid audience")
20+
}
21+
22+
// Verify 'iss' claim
23+
iss := "https://" + os.Getenv("AUTH0_DOMAIN") + "/"
24+
checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
25+
if !checkIss {
26+
return token, errors.New("invalid issuer")
27+
}
28+
29+
cert, err := getPemCert(token)
30+
if err != nil {
31+
return token, err
32+
}
33+
34+
return jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
35+
},
36+
SigningMethod: jwt.SigningMethodRS256,
37+
})
38+
39+
type Jwks struct {
40+
Keys []JSONWebKeys `json:"keys"`
41+
}
42+
43+
type JSONWebKeys struct {
44+
Kty string `json:"kty"`
45+
Kid string `json:"kid"`
46+
Use string `json:"use"`
47+
N string `json:"n"`
48+
E string `json:"e"`
49+
X5c []string `json:"x5c"`
50+
}
51+
52+
func getPemCert(token *jwt.Token) (string, error) {
53+
resp, err := http.Get("https://" + os.Getenv("AUTH0_DOMAIN") + "/.well-known/jwks.json")
54+
if err != nil {
55+
return "", err
56+
}
57+
defer resp.Body.Close()
58+
59+
var jwks Jwks
60+
if err = json.NewDecoder(resp.Body).Decode(&jwks); err != nil {
61+
return "", err
62+
}
63+
64+
var cert string
65+
for _, key := range jwks.Keys {
66+
if token.Header["kid"] == key.Kid {
67+
cert = "-----BEGIN CERTIFICATE-----\n" + key.X5c[0] + "\n-----END CERTIFICATE-----"
68+
break
69+
}
70+
}
71+
72+
if cert == "" {
73+
return cert, errors.New("unable to find appropriate key")
74+
}
75+
76+
return cert, nil
77+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package router
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"strings"
7+
8+
"github.com/codegangsta/negroni"
9+
"github.com/form3tech-oss/jwt-go"
10+
"github.com/gorilla/mux"
11+
12+
"01-Authorization-RS256/middleware"
13+
)
14+
15+
type Response struct {
16+
Message string `json:"message"`
17+
}
18+
19+
func New() *mux.Router {
20+
r := mux.NewRouter()
21+
22+
// This route is always accessible
23+
r.Handle("/api/public", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24+
message := "Hello from a public endpoint! You don't need to be authenticated to see this."
25+
responseJSON(message, w, http.StatusOK)
26+
}))
27+
28+
// This route is only accessible if the user has a valid access_token
29+
// We are chaining the jwtmiddleware middleware into the negroni handler function which will check
30+
// for a valid token.
31+
r.Handle("/api/private", negroni.New(
32+
negroni.HandlerFunc(middleware.JWT.HandlerWithNext),
33+
negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
34+
message := "Hello from a private endpoint! You need to be authenticated to see this."
35+
responseJSON(message, w, http.StatusOK)
36+
}))))
37+
38+
// This route is only accessible if the user has a valid access_token with the read:messages scope
39+
// We are chaining the jwtmiddleware middleware into the negroni handler function which will check
40+
// for a valid token and scope.
41+
r.Handle(
42+
"/api/private-scoped",
43+
negroni.New(
44+
negroni.HandlerFunc(middleware.JWT.HandlerWithNext),
45+
negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46+
token := r.Context().Value("user").(*jwt.Token)
47+
48+
hasScope := checkScope("read:messages", token)
49+
if !hasScope {
50+
message := "Insufficient scope."
51+
responseJSON(message, w, http.StatusForbidden)
52+
return
53+
}
54+
55+
message := "Hello from a private endpoint! You need to be authenticated to see this."
56+
responseJSON(message, w, http.StatusOK)
57+
})),
58+
),
59+
)
60+
61+
return r
62+
}
63+
64+
func checkScope(scope string, token *jwt.Token) bool {
65+
claims, ok := token.Claims.(jwt.MapClaims)
66+
if !ok {
67+
return false
68+
}
69+
70+
const scopeKey = "scope"
71+
tokenScope, ok := claims[scopeKey].(string)
72+
if !ok {
73+
return false
74+
}
75+
76+
result := strings.Split(tokenScope, " ")
77+
for i := range result {
78+
if result[i] == scope {
79+
return true
80+
}
81+
}
82+
83+
return false
84+
}
85+
86+
func responseJSON(message string, w http.ResponseWriter, statusCode int) {
87+
response := Response{message}
88+
89+
jsonResponse, err := json.Marshal(response)
90+
if err != nil {
91+
http.Error(w, err.Error(), http.StatusInternalServerError)
92+
return
93+
}
94+
95+
w.Header().Set("Content-Type", "application/json")
96+
w.WriteHeader(statusCode)
97+
w.Write(jsonResponse)
98+
}

0 commit comments

Comments
 (0)