Skip to content

Commit a55c17b

Browse files
authored
CORS Support (#6)
1 parent e91c2a9 commit a55c17b

File tree

17 files changed

+578
-371
lines changed

17 files changed

+578
-371
lines changed

charts/identity/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: A Helm chart for deploying Unikorn's IdP
44

55
type: application
66

7-
version: v0.1.6
8-
appVersion: v0.1.6
7+
version: v0.1.7
8+
appVersion: v0.1.7
99

1010
icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png

charts/identity/templates/deployment.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ spec:
2020
args:
2121
- --namespace={{ .Release.Namespace }}
2222
- --host=https://{{ .Values.host }}
23+
{{- with $cors := .Values.cors }}
24+
{{- range $origin := $cors.allowOrigin }}
25+
{{ printf "- --cors-allow-origin=%s" $origin | nindent 8 }}
26+
{{- end }}
27+
{{- if $cors.maxAge }}
28+
{{ printf "- --cors-max-age=%s" $cors.maxAge | nindent 8 }}
29+
{{- end }}
30+
{{- end }}
2331
volumeMounts:
2432
- name: unikorn-identity-jose-tls
2533
mountPath: /var/lib/secrets/unikorn-cloud.org/jose

charts/identity/values.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,10 @@ ingress:
8282

8383
# If true, will add the external DNS hostname annotation.
8484
externalDns: false
85+
86+
# Allows CORS to be configured/secured
87+
# cors:
88+
# # Broswers must send requests from these origin servers, defaults to * if not set.
89+
# allowOrigin: ['*']
90+
# # How long to cache the CORS preflight for, mostly useless as browsers override this.
91+
# maxAge: 86400

openapi/server.spec.yaml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ paths:
3030
responses:
3131
'200':
3232
$ref: '#/components/responses/authorizationResponse'
33-
# TODO: returns HTML not JSON
33+
'302':
34+
description: A redirect back to the validated callback URL.
3435
'400':
35-
$ref: '#/components/responses/badRequestResponse'
36+
$ref: '#/components/responses/htmlErrorResponse'
3637
'500':
37-
$ref: '#/components/responses/internalServerErrorResponse'
38+
$ref: '#/components/responses/htmlErrorResponse'
3839
/oauth2/v2/token:
3940
description: |-
4041
Implements OAuth2 code exchange.
@@ -603,6 +604,11 @@ components:
603604
example:
604605
error: conflict
605606
error_description: a resource with the same name already exists
607+
htmlErrorResponse:
608+
description: |-
609+
A generic HTML error page.
610+
content:
611+
text/html: {}
606612
internalServerErrorResponse:
607613
description: |-
608614
An unexpected error occurred, this may be an unexpected transient error and

pkg/generated/client.go

Lines changed: 0 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/generated/schema.go

Lines changed: 111 additions & 110 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/handler/handler.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,7 @@ func (h *Handler) setUncacheable(w http.ResponseWriter) {
6262
w.Header().Add("Cache-Control", "no-cache")
6363
}
6464

65-
func (h *Handler) setCORS(w http.ResponseWriter) {
66-
w.Header().Add("Access-Control-Allow-Origin", "*")
67-
}
68-
6965
func (h *Handler) GetWellKnownOpenidConfiguration(w http.ResponseWriter, r *http.Request) {
70-
h.setCORS(w)
71-
7266
result := &generated.OpenidConfiguration{
7367
Issuer: h.options.Host,
7468
AuthorizationEndpoint: fmt.Sprintf("%s/oauth2/v2/authorization", h.options.Host),
@@ -122,8 +116,6 @@ func (h *Handler) PostOauth2V2Login(w http.ResponseWriter, r *http.Request) {
122116
}
123117

124118
func (h *Handler) PostOauth2V2Token(w http.ResponseWriter, r *http.Request) {
125-
h.setCORS(w)
126-
127119
result, err := h.authenticator.OAuth2.Token(w, r)
128120
if err != nil {
129121
errors.HandleError(w, r, err)
@@ -135,8 +127,6 @@ func (h *Handler) PostOauth2V2Token(w http.ResponseWriter, r *http.Request) {
135127
}
136128

137129
func (h *Handler) GetOauth2V2Jwks(w http.ResponseWriter, r *http.Request) {
138-
h.setCORS(w)
139-
140130
result, err := h.authenticator.JWKS()
141131
if err != nil {
142132
errors.HandleError(w, r, err)

pkg/middleware/cors/cors.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Copyright 2024 the Unikorn Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cors
18+
19+
import (
20+
"net/http"
21+
"strconv"
22+
"strings"
23+
24+
"github.com/spf13/pflag"
25+
26+
"github.com/unikorn-cloud/core/pkg/util"
27+
"github.com/unikorn-cloud/identity/pkg/errors"
28+
"github.com/unikorn-cloud/identity/pkg/middleware/openapi"
29+
)
30+
31+
type Options struct {
32+
AllowedOrigins []string
33+
MaxAge int
34+
}
35+
36+
func (o *Options) AddFlags(f *pflag.FlagSet) {
37+
f.StringSliceVar(&o.AllowedOrigins, "--cors-allow-origin", []string{"*"}, "CORS allowed origins")
38+
f.IntVar(&o.MaxAge, "--cors-max-age", 86400, "CORS maximum age (may be overridden by the browser)")
39+
}
40+
41+
func Middleware(schema *openapi.Schema, options *Options) func(http.Handler) http.Handler {
42+
return func(next http.Handler) http.Handler {
43+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
44+
// All requests get the allow origin header.
45+
for _, origin := range options.AllowedOrigins {
46+
w.Header().Add("Access-Control-Allow-Origin", origin)
47+
}
48+
49+
// For normal requests handle them.
50+
if r.Method != http.MethodOptions {
51+
next.ServeHTTP(w, r)
52+
return
53+
}
54+
55+
// Handle preflight
56+
method := r.Header.Get("Access-Control-Request-Method")
57+
if method == "" {
58+
errors.HandleError(w, r, errors.OAuth2InvalidRequest("OPTIONS missing Access-Control-Request-Method header"))
59+
return
60+
}
61+
62+
request := r.Clone(r.Context())
63+
request.Method = method
64+
65+
route, _, err := schema.FindRoute(request)
66+
if err != nil {
67+
errors.HandleError(w, r, err)
68+
return
69+
}
70+
71+
// TODO: add OPTIONS to the schema?
72+
methods := util.Keys(route.PathItem.Operations())
73+
methods = append(methods, http.MethodOptions)
74+
75+
// TODO: I've tried adding them to the schema, but the generator
76+
// adds them to the hander function signatures, which is superfluous
77+
// to requirements.
78+
headers := []string{
79+
"Authorization",
80+
"traceparent",
81+
"tracestate",
82+
}
83+
84+
w.Header().Add("Access-Control-Allow-Methods", strings.Join(methods, ", "))
85+
w.Header().Add("Access-Control-Allow-Headers", strings.Join(headers, ", "))
86+
w.Header().Add("Access-Control-Max-Age", strconv.Itoa(options.MaxAge))
87+
w.WriteHeader(http.StatusNoContent)
88+
})
89+
}
90+
}

pkg/middleware/logging.go

Lines changed: 0 additions & 178 deletions
This file was deleted.

0 commit comments

Comments
 (0)