diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 94210e5..52a6c1e 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -45,13 +45,20 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=tag + type=ref,event=pr + type=semver,pattern={{raw}} + type=raw,value={{sha}} + type=ref,event=branch - name: Build and push Docker image id: build-and-push uses: docker/build-push-action@v5 with: context: . - push: ${{ github.event_name != 'pull_request'}} + push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha diff --git a/Dockerfile b/Dockerfile index 29a54bc..a6d63ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,24 +2,23 @@ FROM golang:alpine AS plugin-build COPY plugins /tmp/plugins WORKDIR /tmp/plugins RUN mkdir /plugins -RUN for d in */ ; do echo "Bulding plugin in $d"; cd $d; go mod download -x -json; go build -o /plugins/ -x . ; echo "Built plugin in $d"; cd ../; done +RUN for d in */ ; do echo "Bulding plugin in $d"; cd $d; go mod download -x -json; go build -x -v -o /plugins/ -x . ; echo "Built plugin in $d"; cd ../; done RUN ls /plugins FROM kong:alpine USER root -COPY --from=plugin-build /plugins /usr/local/kong/ -USER kong +COPY --from=plugin-build /plugins /usr/local/bin/ +RUN chown kong:kong /usr/local/bin/oidc ENV KONG_PLUGINSERVER_NAMES=oidc -ENV KONG_PLUGINSERVER_OIDC_QUERY_CMD="/usr/local/kong/oidc -dump" -ENV KONG_PROXY_LISTEN="0.0.0.0:8000 http2 reuseport backlog=16384" -ENV KONG_ADMIN_LISTEN="0.0.0.0:8001 http2 reuseport backlog=16384" -ENV KONG_PLUGINS=oidc,bundled +ENV KONG_PLUGINSERVER_OIDC_START_CMD="/usr/local/bin/oidc" +ENV KONG_PLUGINSERVER_OIDC_SOCKET="/usr/local/kong/oidc.socket" +ENV KONG_PLUGINSERVER_OIDC_QUERY_CMD="/usr/local/bin/oidc -dump" +ENV KONG_PROXY_LISTEN="0.0.0.0:8000 reuseport backlog=16384" +ENV KONG_ADMIN_LISTEN="0.0.0.0:8001 reuseport backlog=16384" +ENV KONG_GUI_LISTEN="0.0.0.0:8002" +ENV KONG_PLUGINS=bundled,oidc ENV KONG_PROXY_ACCESS_LOG=/dev/stdout ENV KONG_ADMIN_ACCESS_LOG=/dev/stdout ENV KONG_PROXY_ERROR_LOG=/dev/stderr ENV KONG_ADMIN_ERROR_LOG=/dev/stderr -ENTRYPOINT ["/docker-entrypoint.sh"] -EXPOSE 8000 8001 -STOPSIGNAL SIGQUIT -HEALTHCHECK --interval=10s --timeout=10s --retries=10 CMD kong health -CMD ["kong", "docker-start"] \ No newline at end of file +LABEL org.opencontainers.image.source="https://github.com/wisdom-oss/api-gateway" \ No newline at end of file diff --git a/README.md b/README.md index 8c79a09..f6c0514 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,28 @@ -

API Gateway

-

api-gateway

-

-🛡️ A Kong API Gateway extended with functionality for the WISdoM -platform -

+ +

API Gateway

+

api-gateway

+

🛡️ Extended Kong API Gateway for the WISdoM Platform

+GitHub Actions Workflow Status + +Static Badge +
-# About -The [WISdoM project](https://github.com/wisdom-oss) utilizes the Kong API -Gateway to route requests to their services. Since some needed functionality of -the gateway is locked behind paid plugins, this repository contains some custom -plugins which implement the following features: +> [!NOTE] +> This API Gateway is based on the Open Source Edition of the [Kong API Gateway](https://konghq.com/products/kong-gateway). +> Therefore, features available in the free (closed-source) and enterprise (also +> closed-source) edition are not available with this image. -- [x] OpenID Connect Authentication/Authorization with JWT Validation +The API Gateway is the central part of the WISdoM Plaform and manages the access +to the microservices implemented for the platform. +Furhtermore, it is extended by a plugin using the +[`go-pdk`](https://github.com/Kong/go-pdk) to validate JWTs which are used in a +standard deployment to authenticate and secure requests. +> — [Read more](plugins/oidc/README.md) -The plugins are implemented in Golang using IPC for communicating with the -API Gateway. - -## How to use -Since the API gateway in included in every deployment of the WISdoM platform, -there are no extra steps you need to take. \ No newline at end of file +The API Gateway is autommatically configured using the +[gateway-service-watcher](https://github.com/wisdom-oss/gateway-service-watcher) +which acts as a watchdog to check the deployed containers on the host for their +association to the WISdoM platform. +> — [Read more](https://github.com/wisdom-oss/gateway-service-watcher) \ No newline at end of file diff --git a/plugins/oidc/.gitignore b/plugins/oidc/.gitignore new file mode 100644 index 0000000..d7c3743 --- /dev/null +++ b/plugins/oidc/.gitignore @@ -0,0 +1,2 @@ +.idea +*.exe \ No newline at end of file diff --git a/plugins/oidc/README.md b/plugins/oidc/README.md index 5265c14..f6c8633 100644 --- a/plugins/oidc/README.md +++ b/plugins/oidc/README.md @@ -20,5 +20,7 @@ These responses can optionally be cached using a redis database to further minimize the number of network requests executed for each intercepted request. ## Configuration -The configuration is done automatically during the startup of the api gateway -using the environment variables passed to the api gateway \ No newline at end of file +The configuration is done by the +[watchdog](https://github.com/wisdom-oss/watchdog) on a service-by-service +basis. +Please refer to the documentation of the watchdog for further information. \ No newline at end of file diff --git a/plugins/oidc/access.go b/plugins/oidc/access.go index 48d5017..549550f 100644 --- a/plugins/oidc/access.go +++ b/plugins/oidc/access.go @@ -4,124 +4,251 @@ import ( "context" "encoding/json" "errors" + "fmt" "net/http" + "reflect" "regexp" "strconv" "strings" "github.com/Kong/go-pdk" + "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" ) -var headerRegex = regexp.MustCompile(`^(\w+) (\S+)$`) +// bearerTokenPattern is a regular expression pattern used to validate and parse +// bearer tokens. +// It matches the following format: "Bearer ". The token is a non-empty +// string of characters that do not contain whitespace. +// The pattern is case-insensitive. +// Example usage: +// +// if matched, _ := regexp.MatchString(bearerTokenPattern, token); matched { +// // Token is valid +// } else { +// // Token is invalid +// } +const bearerTokenPattern = `(?i)^(Bearer) (\S+)$` -// Access is executed every time the kong gateway receives a request. Since the +// bearerTokenRegEx is used to store the regular expression after building it +// once +var bearerTokenRegEx *regexp.Regexp + +var errEmptyEndpoint = errors.New("empty discovery endpoint") +var errDiscoveryRequestFailure = errors.New("discovery request failed") +var errDiscoveryUnmarshalFailure = errors.New("unable to unmarshal openid connect configuration") +var errDiscoveryInvalidType = errors.New("discovery response contained invalid type") + +// fieldTypeErrorTemplate is used to keep the same repeating string as a +// constant before creating error messages with it +const fieldTypeErrorTemplate = `field '%s' is expected to be 'string' got: %s` + +// discoverEndpoints retrieves the userinfo and jwks endpoints from the given +// discovery endpoint. +// The function sends a GET request to the discovery endpoint, which should +// respond with the openid connect configuration. +// This configuration is a JSON object that the function decodes into a map, +// extracting the userinfo_endpoint and jwks_uri fields as the userinfo and jwks +// endpoints respectively. +// In case of error, it will also join the error with suitable contextual error +// message and return. +// If the discovery endpoint is empty or leads to a failed request, the function +// returns an error. +// If the retrieval or decoding of the openid connect configuration fails, the +// function returns an error. +// +// Parameters: +// +// discoveryEndpoint (string): address of the discovery endpoint. +// +// Returns: +// +// userinfoEndpoint (string): the discovered userinfo endpoint. +// jwksEndpoint (string): the discovered jwks endpoint. +// err (error): the error, if any occurred during the process. +func discoverEndpoints(discoveryEndpoint string) (userinfoEndpoint string, jwksEndpoint string, err error) { + // cleanup the discovery endpoint address and remove possible whitespaces + discoveryEndpoint = strings.TrimSpace(discoveryEndpoint) + if discoveryEndpoint == "" { + return "", "", errEmptyEndpoint + } + + // now request the openid connect configuration endpoint + res, err := http.Get(discoveryEndpoint) + if err != nil { + return "", "", errors.Join(errDiscoveryRequestFailure, err) + } + + // now read the openid connect configuration + var openIdConnectConfiguration map[string]interface{} + err = json.NewDecoder(res.Body).Decode(&openIdConnectConfiguration) + if err != nil { + return "", "", errors.Join(err, errDiscoveryUnmarshalFailure) + } + var isValidString bool + userinfoEndpoint, isValidString = openIdConnectConfiguration["userinfo_endpoint"].(string) + if !isValidString { + actualType := reflect.TypeOf(openIdConnectConfiguration["userinfo_endpoint"]) + return "", "", errors.Join(errDiscoveryInvalidType, fmt.Errorf(fieldTypeErrorTemplate, "userinfo_endpoint", actualType)) + } + + jwksEndpoint, isValidString = openIdConnectConfiguration["jwks_uri"].(string) + if !isValidString { + actualType := reflect.TypeOf(openIdConnectConfiguration["jwks_uri"]) + return "", "", errors.Join(errDiscoveryInvalidType, fmt.Errorf(fieldTypeErrorTemplate, "jwks_uri", actualType)) + } + + return userinfoEndpoint, jwksEndpoint, nil +} + +// extractBearerToken is a function that takes an authorization header value as +// input and extracts the bearer token from it. +// It uses a regular expression to match the header value against a predefined +// pattern. +// If a match is found and there are two capture groups in the match result, the +// function returns the second capture group, which represents the bearer token. +// If no match is found or there are not exactly two capture groups, an empty +// string is returned. +// +// Example usage: +// +// token := extractBearerToken("Bearer abcxyz123") +// // token = "abcxyz123" +// +// Parameters: +// - authorizationHeaderValue: The value of the authorization header. +// +// Returns: +// +// The extracted bearer token, or an empty string if no valid bearer token is found. +func extractBearerToken(authorizationHeaderValue string) string { + // check if the bearer token regex has already been compiled + if bearerTokenRegEx == nil { + bearerTokenRegEx = regexp.MustCompile(bearerTokenPattern) + } + matches := bearerTokenRegEx.FindStringSubmatch(authorizationHeaderValue) + if len(matches) == 3 { + return matches[2] + } + return "" +} + +// Access is executed every time the kong gateway receives a request. Since ther // plugin may be restarted at any moment func (c *Configuration) Access(kong *pdk.PDK) { - // access the request and get the authorization header - request := kong.Request + // create a shortcut for the logger logger := kong.Log - logger.Info("authenticating new request") - logger.Debug("extracting authorization header and its values") - authorizationHeader, _ := request.GetHeader("Authorization") - // now trim away any blank values at the start or end of the string - authorizationHeader = strings.TrimSpace(authorizationHeader) - // now get the authorization method and token from the header - matches := headerRegex.FindStringSubmatch(authorizationHeader) - if len(matches) != 3 { - // since the matches array needs to contain three entries, the value is - // invalid and therefore the user is not authorized + + userinfoEndpoint, jwksEndpoint, err := discoverEndpoints(c.DiscoveryUri) + if err != nil { + logger.Crit("unable to extract required endpoints from openid connect discovery", err) response := GatewayError{ - ErrorCode: "gateway.MALFORMED_AUTHORIZATION_HEADER", - ErrorTitle: "Authorization Header Malformed", - ErrorDescription: "The 'Authorization' header is not correctly formatted. please check your request", - HttpStatusCode: 400, - HttpStatusText: "Bad Request", + HttpStatusCode: 500, + HttpStatusText: "InternalServerError", } - headers := map[string][]string{ - "WWW-Authenticate": {`Bearer scope="openid profile email", error="invalid_request", error_description="Authorization header malformed'"`}, + switch { + case errors.Is(err, errEmptyEndpoint): + response = GatewayError{ + ErrorCode: "gateway.OPENID_CONNECT_INVALID_DISCOVERY_ENDPOINT", + ErrorTitle: "Invalid Discovery Endpoint", + ErrorDescription: "The configured OpenID Connect discovery endpoint is invalid: " + err.Error(), + } + case errors.Is(err, errDiscoveryRequestFailure): + response = GatewayError{ + ErrorCode: "gateway.OPENID_CONNECT_DISCOVERY_REQUEST_FAILURE", + ErrorTitle: "Discovery Request Failure", + ErrorDescription: "The discovery request needed for access token validation failed: " + err.Error(), + } + case errors.Is(err, errDiscoveryUnmarshalFailure): + response = GatewayError{ + ErrorCode: "gateway.OPENID_CONNECT_DISCOVERY_RESPONSE_PARSE_ERROR", + ErrorTitle: "Discovery Response Parsing Error", + ErrorDescription: "The discovery response sent by the configured endpoint could not be parsed: " + err.Error(), + } } - response.SendError(kong, headers) - return + response.SendError(kong, nil) } - // now extract the authorization method and the token - authorizationMethod := matches[1] - token := matches[2] - logger.Debug("extracted authorization method and token") - logger.Debug("validating authorization method") - // now check if the authorization method is Bearer - if authorizationMethod != "Bearer" { + + // use the extracted JWKS endpoint to download the JSON Web Key Set to be + // able to verify the access token locally + jwks, err := jwk.Fetch(context.Background(), jwksEndpoint) + if err != nil { + logger.Crit("unable to fetch the JWKS", err.Error()) response := GatewayError{ - ErrorCode: "gateway.INVALID_AUTH_SCHEME", - ErrorTitle: "Authorization Header Malformed", - ErrorDescription: "The 'Authorization' does not use the 'Bearer' auth scheme", + ErrorCode: "gateway.OPENID_CONNECT_JWKS_DOWNLOAD_ERROR", + ErrorTitle: "JWKS Download Error", + ErrorDescription: "Unable to download the JWKS needed for validating the access token: " + err.Error(), + HttpStatusCode: 500, + HttpStatusText: "Internal Server Error", + } + response.SendError(kong, nil) + } + + authorizationHeader, err := kong.Request.GetHeader("Authorization") + if err != nil { + logger.Crit("unable to extract headers from request", err.Error()) + response := GatewayError{ + ErrorCode: "gateway.HEADER_EXTRACTION_FAILED", + ErrorTitle: "Header Extraction Failure", + ErrorDescription: "Unable to extract required request headers from the incoming request", + HttpStatusCode: 500, + HttpStatusText: "Internal Server Error", + } + response.SendError(kong, nil) + } + + bearerToken := extractBearerToken(authorizationHeader) + if strings.TrimSpace(bearerToken) == "" { + logger.Warn("empty bearer token extracted from request") + response := GatewayError{ + ErrorCode: "gateway.EMPTY_BEARER_TOKEN", + ErrorTitle: "Empty Bearer Token", + ErrorDescription: "The Bearer Token extracted from the request is empty.", HttpStatusCode: 400, HttpStatusText: "Bad Request", } - headers := map[string][]string{ + response.SendError(kong, map[string][]string{ "WWW-Authenticate": {`Bearer scope="openid profile email", error="invalid_request", error_description="Authorization header malformed'"`}, - } - response.SendError(kong, headers) - return + }) } - logger.Debug("validated authorization method") - // now take the extracted token and parse it using the jwks and validate it - logger.Debug("parsing and validating jwt from headers") - jwks, err := JWKSCache.Get(context.Background(), JWKSUrl) - if err != nil { - logger.Err("error while getting jwks", err) - response := GatewayError{} - response.WrapError(err, kong) - return - } - accessToken, err := jwt.ParseString(token, jwt.WithKeySet(jwks), jwt.WithAudience(OIDC_ClientID), jwt.WithValidate(true)) + // now parse the bearer token and automatically validate it + accessToken, err := jwt.ParseString(bearerToken, jwt.WithKeySet(jwks), jwt.WithAudience(c.ClientID), jwt.WithValidate(true)) if err != nil { // prepare an error response - response := GatewayError{} + response := GatewayError{ + HttpStatusCode: http.StatusUnauthorized, + HttpStatusText: http.StatusText(http.StatusUnauthorized), + } switch { case errors.Is(err, jwt.ErrTokenExpired()): response.ErrorCode = "gateway.TOKEN_EXPIRED" response.ErrorTitle = "Access Token Expired" response.ErrorDescription = "The access token in the 'Authorization' header has expired" - response.HttpStatusCode = http.StatusUnauthorized - response.HttpStatusText = http.StatusText(response.HttpStatusCode) - break case errors.Is(err, jwt.ErrInvalidIssuedAt()): response.ErrorCode = "gateway.TOKEN_ISSUED_IN_FUTURE" response.ErrorTitle = "Access Token Issued In Future" response.ErrorDescription = "The access token in the 'Authorization' header has been issued in the future" - response.HttpStatusCode = http.StatusUnauthorized - response.HttpStatusText = http.StatusText(response.HttpStatusCode) - break case errors.Is(err, jwt.ErrTokenNotYetValid()): response.ErrorCode = "gateway.TOKEN_USED_TOO_EARLY" response.ErrorTitle = "Access Token Used Too Early" response.ErrorDescription = "The access token in the 'Authorization' header is not valid yet. please try again later" - response.HttpStatusCode = http.StatusUnauthorized - response.HttpStatusText = http.StatusText(response.HttpStatusCode) - break case errors.Is(err, jwt.ErrInvalidAudience()): response.ErrorCode = "gateway.TOKEN_INVALID_AUDIENCE" response.ErrorTitle = "Access Token Invalid Audience" response.ErrorDescription = "The access token in the 'Authorization' header has not been issued for this platform" - response.HttpStatusCode = http.StatusUnauthorized - response.HttpStatusText = http.StatusText(response.HttpStatusCode) - break default: - logger.Err("unknown error occurred while checking jwt", err) + logger.Crit("unknown error occurred while checking jwt", err.Error()) response.WrapError(err, kong) return } response.SendError(kong, nil) } - logger.Debug("validated jwt") // since the access token now has been validated to be active, request the // userinfo endpoint using the access token as Authorization - logger.Debug("requesting userinfo") - userinfoRequest, err := http.NewRequest("GET", UserinfoEndpoint, nil) + userinfoRequest, err := http.NewRequest("GET", userinfoEndpoint, nil) if err != nil { - logger.Err("unable to create new request for userinfo", err) + logger.Crit("unable to create new request for userinfo", err) response := GatewayError{} response.WrapError(err, kong) return @@ -130,17 +257,16 @@ func (c *Configuration) Access(kong *pdk.PDK) { httpClient := http.Client{} userinfoResponse, err := httpClient.Do(userinfoRequest) if err != nil { - logger.Err("error while getting userinfo", err) + logger.Crit("error while getting userinfo", err) response := GatewayError{} response.WrapError(err, kong) return } // now parse the userinfo response - logger.Debug("parsing userinfo response") var userinfo map[string]interface{} err = json.NewDecoder(userinfoResponse.Body).Decode(&userinfo) if err != nil { - logger.Err("error while parsing userinfo", err) + logger.Crit("error while parsing userinfo", err) response := GatewayError{} response.WrapError(err, kong) return @@ -150,7 +276,7 @@ func (c *Configuration) Access(kong *pdk.PDK) { subject, isSet := userinfo["sub"].(string) if !isSet { err := errors.New("userinfo missing 'subject'") - logger.Err("invalid userinfo response", err) + logger.Crit("invalid userinfo response", err) response := GatewayError{} response.WrapError(err, kong) return @@ -159,7 +285,7 @@ func (c *Configuration) Access(kong *pdk.PDK) { // now check if the userinfo really is for the access token if accessToken.Subject() != subject { err := errors.New("userinfo subject mismatch") - logger.Err("invalid userinfo response", err) + logger.Crit("invalid userinfo response", err) response := GatewayError{} response.WrapError(err, kong) return @@ -169,7 +295,7 @@ func (c *Configuration) Access(kong *pdk.PDK) { username, isSet := userinfo["preferred_username"].(string) if !isSet { err := errors.New("userinfo missing 'preferred_username'") - logger.Err("invalid userinfo response", err) + logger.Crit("invalid userinfo response", err) response := GatewayError{} response.WrapError(err, kong) return @@ -203,24 +329,83 @@ func (c *Configuration) Access(kong *pdk.PDK) { // now check if the userinfo response contains the staff marker staffString, isSet := userinfo["staff"].(string) if !isSet { - kong.ServiceRequest.SetHeader("X-Is-Staff", "false") - kong.ServiceRequest.SetHeader("X-Superuser", "false") + err := kong.ServiceRequest.SetHeader("X-Is-Staff", "false") + if err != nil { + logger.Err("unable to set staff header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } + err = kong.ServiceRequest.SetHeader("X-Superuser", "false") + if err != nil { + logger.Err("unable to set superuser header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } } else { // try to parse the staff string into a boolean isStaff, err := strconv.ParseBool(staffString) if err != nil || !isStaff { - kong.ServiceRequest.SetHeader("X-Is-Staff", "false") - kong.ServiceRequest.SetHeader("X-Superuser", "false") - + err := kong.ServiceRequest.SetHeader("X-Is-Staff", "false") + if err != nil { + logger.Err("unable to set staff header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } + err = kong.ServiceRequest.SetHeader("X-Superuser", "false") + if err != nil { + logger.Err("unable to set superuser header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } } else { - kong.ServiceRequest.SetHeader("X-Is-Staff", "true") - kong.ServiceRequest.SetHeader("X-Superuser", "true") + err := kong.ServiceRequest.SetHeader("X-Is-Staff", "true") + if err != nil { + logger.Err("unable to set staff header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } + err = kong.ServiceRequest.SetHeader("X-Superuser", "true") + if err != nil { + logger.Err("unable to set superuser header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } } } // now add the headers to the downstream request, which set the groups - kong.ServiceRequest.SetHeader("X-WISdoM-User", username) - kong.ServiceRequest.SetHeader("X-Authenticated-User", username) - kong.ServiceRequest.SetHeader("X-WISdoM-Groups", groupString) - kong.ServiceRequest.SetHeader("X-Authenticated-Groups", groupString) + err = kong.ServiceRequest.SetHeader("X-WISdoM-User", username) + if err != nil { + logger.Err("unable to set username header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } + err = kong.ServiceRequest.SetHeader("X-Authenticated-User", username) + if err != nil { + logger.Err("unable to set authenticated user header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } + err = kong.ServiceRequest.SetHeader("X-WISdoM-Groups", groupString) + if err != nil { + logger.Err("unable to set groups header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } + err = kong.ServiceRequest.SetHeader("X-Authenticated-Groups", groupString) + if err != nil { + logger.Err("unable to set authenticated groups header", err) + response := GatewayError{} + response.WrapError(err, kong) + return + } } diff --git a/plugins/oidc/config.go b/plugins/oidc/config.go index 05dec0f..b85d7d0 100644 --- a/plugins/oidc/config.go +++ b/plugins/oidc/config.go @@ -1,4 +1,6 @@ package main type Configuration struct { + DiscoveryUri string `json:"discoveryUri"` + ClientID string `json:"clientID"` } diff --git a/plugins/oidc/globals.go b/plugins/oidc/globals.go deleted file mode 100644 index 8a910ea..0000000 --- a/plugins/oidc/globals.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import "github.com/lestrrat-go/jwx/v2/jwk" - -var JWKSCache *jwk.Cache -var JWKSUrl string -var OIDC_ClientID string -var UserinfoEndpoint string diff --git a/plugins/oidc/init.go b/plugins/oidc/init.go deleted file mode 100644 index 73e703b..0000000 --- a/plugins/oidc/init.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "log" - "net/http" - "os" - - "github.com/lestrrat-go/jwx/v2/jwk" -) - -func init() { - // get the environment variable pointing to the openid connect configuration - // on the authorization server - oidcConnectUrl, isSet := os.LookupEnv("OIDC_AUTHORITY") - if !isSet { - log.Fatal("OIDC_AUTHORITY environment variable is not set") - } - - // get the configuration of the openid connect server - res, err := http.Get(oidcConnectUrl) - if err != nil { - log.Fatal("unable to load openid connect configuration") - } - var oidcConfiguration map[string]interface{} - err = json.NewDecoder(res.Body).Decode(&oidcConfiguration) - if err != nil { - log.Fatal("unable to parse open id connect configuration") - } - - // now retrieve the jwks url and the introspection url - UserinfoEndpoint = oidcConfiguration["userinfo_endpoint"].(string) - JWKSUrl = oidcConfiguration["jwks_uri"].(string) - // now download the jwks used to sign the access tokens - JWKSCache = jwk.NewCache(context.Background()) - err = JWKSCache.Register(JWKSUrl) - if err != nil { - log.Fatal("unable to register JWKS URL in JWKSCache") - } - - // now get the Client ID - OIDC_ClientID, isSet = os.LookupEnv("OIDC_CLIENT_ID") - if !isSet { - log.Fatal("OIDC_CLIENT_ID environment variable is not set") - } -}