|
1 | 1 | package main |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "encoding/json" |
5 | | - "errors" |
6 | | - "fmt" |
7 | 4 | "log" |
8 | 5 | "net/http" |
9 | | - "os" |
10 | | - "strings" |
11 | 6 |
|
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" |
16 | 7 | "github.com/joho/godotenv" |
17 | 8 | "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 | | -} |
27 | 9 |
|
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 | +) |
36 | 12 |
|
37 | 13 | func main() { |
38 | | - err := godotenv.Load() |
39 | | - if err != nil { |
| 14 | + if err := godotenv.Load(); err != nil { |
40 | 15 | log.Print("Error loading .env file") |
41 | 16 | } |
42 | 17 |
|
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 | | - |
69 | 18 | c := cors.New(cors.Options{ |
70 | 19 | AllowedOrigins: []string{"http://localhost:3000"}, |
71 | 20 | AllowCredentials: true, |
72 | 21 | AllowedHeaders: []string{"Authorization"}, |
73 | 22 | }) |
74 | 23 |
|
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() |
112 | 25 | 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 | | - } |
123 | 26 |
|
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) |
128 | 30 | } |
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) |
182 | 31 | } |
0 commit comments