Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions backend/.mockery.private.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,19 @@ packages:
structname: '{{.InterfaceName}}Mock'
pkgname: cache
filename: "{{.InterfaceName}}_mock_test.go"

github.com/asgardeo/thunder/internal/oauth/oauth2/introspect:
config:
all: true
dir: internal/oauth/oauth2/introspect
structname: '{{.InterfaceName}}Mock'
pkgname: introspect
filename: "{{.InterfaceName}}_mock_test.go"

github.com/asgardeo/thunder/internal/oauth/oauth2/authz:
config:
all: true
dir: internal/oauth/oauth2/authz
structname: '{{.InterfaceName}}Mock'
pkgname: authz
filename: "{{.InterfaceName}}_mock_test.go"
38 changes: 11 additions & 27 deletions backend/.mockery.public.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,28 +93,12 @@ packages:
pkgname: jwksmock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/oauth/scope/provider:
github.com/asgardeo/thunder/internal/oauth/scope:
config:
all: true
dir: tests/mocks/oauth/scope/providermock
dir: tests/mocks/oauth/scopemock
structname: '{{.InterfaceName}}Mock'
pkgname: providermock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/oauth/scope/validator:
config:
all: true
dir: tests/mocks/oauth/scope/validatormock
structname: '{{.InterfaceName}}Mock'
pkgname: validatormock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/oauth/session/store:
config:
all: true
dir: tests/mocks/oauth/session/storemock
structname: '{{.InterfaceName}}Mock'
pkgname: storemock
pkgname: scopemock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/oauth/oauth2/authz:
Expand All @@ -125,14 +109,6 @@ packages:
pkgname: authzmock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/oauth/oauth2/authz/store:
config:
all: true
dir: tests/mocks/oauth/oauth2/authz/storemock
structname: '{{.InterfaceName}}Mock'
pkgname: storemock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/oauth/oauth2/granthandlers:
config:
all: true
Expand Down Expand Up @@ -244,3 +220,11 @@ packages:
structname: '{{.InterfaceName}}Mock'
pkgname: httpmock
filename: "{{.InterfaceName}}_mock.go"

github.com/asgardeo/thunder/internal/application:
config:
all: true
dir: tests/mocks/applicationmock
structname: '{{.InterfaceName}}Mock'
pkgname: applicationmock
filename: "{{.InterfaceName}}_mock.go"
16 changes: 4 additions & 12 deletions backend/cmd/server/servicemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/asgardeo/thunder/internal/group"
"github.com/asgardeo/thunder/internal/idp"
"github.com/asgardeo/thunder/internal/notification"
"github.com/asgardeo/thunder/internal/oauth"
"github.com/asgardeo/thunder/internal/ou"
"github.com/asgardeo/thunder/internal/system/jwt"
"github.com/asgardeo/thunder/internal/system/log"
Expand Down Expand Up @@ -63,24 +64,15 @@ func registerServices(mux *http.ServeMux) {

_ = flowexec.Initialize(mux, flowMgtService, applicationService)

// Initialize OAuth services.
oauth.Initialize(mux, applicationService, userService, jwtService)

// TODO: Legacy way of initializing services. These need to be refactored in the future aligning to the
// dependency injection pattern used above.

// Register the health service.
services.NewHealthCheckService(mux)

// Register the token service.
services.NewTokenService(mux)

// Register the authorization service.
services.NewAuthorizationService(mux)

// Register the JWKS service.
services.NewJWKSAPIService(mux)

// Register the introspection service.
services.NewIntrospectionAPIService(mux)

// Register the authentication service.
services.NewAuthenticationService(mux)
}
47 changes: 47 additions & 0 deletions backend/internal/oauth/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// Package oauth provides centralized initialization for all OAuth-related services.
package oauth

import (
"net/http"

"github.com/asgardeo/thunder/internal/application"
"github.com/asgardeo/thunder/internal/oauth/jwks"
"github.com/asgardeo/thunder/internal/oauth/oauth2/granthandlers"
"github.com/asgardeo/thunder/internal/oauth/oauth2/introspect"
"github.com/asgardeo/thunder/internal/oauth/oauth2/token"
"github.com/asgardeo/thunder/internal/oauth/scope"
"github.com/asgardeo/thunder/internal/system/jwt"
"github.com/asgardeo/thunder/internal/user"
)

// Initialize initializes all OAuth-related services and registers their routes.
func Initialize(
mux *http.ServeMux,
applicationService application.ApplicationServiceInterface,
userService user.UserServiceInterface,
jwtService jwt.JWTServiceInterface,
) {
jwks.Initialize(mux)
grantHandlerProvider := granthandlers.Initialize(mux, jwtService, userService, applicationService)
scopeValidator := scope.Initialize()
token.Initialize(mux, applicationService, grantHandlerProvider, scopeValidator)
introspect.Initialize(mux, jwtService)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
* under the License.
*/

// Package constants defines the constants used in the JWKS service.
package constants
package jwks

import "github.com/asgardeo/thunder/internal/system/error/serviceerror"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,32 @@
* under the License.
*/

// Package handler provides the HTTP handler for retrieving JSON Web Key Sets (JWKS).
package handler
package jwks

import (
"encoding/json"
"net/http"

"github.com/asgardeo/thunder/internal/oauth/jwks"
serverconst "github.com/asgardeo/thunder/internal/system/constants"
"github.com/asgardeo/thunder/internal/system/error/apierror"
"github.com/asgardeo/thunder/internal/system/error/serviceerror"
"github.com/asgardeo/thunder/internal/system/log"
)

// JWKSHandler handles requests for the JSON Web Key Set (JWKS).
type JWKSHandler struct {
jwksService jwks.JWKSServiceInterface
// jwksHandler handles requests for the JSON Web Key Set (JWKS).
type jwksHandler struct {
jwksService JWKSServiceInterface
}

// NewJWKSHandler creates a new instance of JWKSHandler.
func NewJWKSHandler() *JWKSHandler {
return &JWKSHandler{
jwksService: jwks.NewJWKSService(),
// newJWKSHandler creates a new instance of jwksHandler.
func newJWKSHandler(jwksService JWKSServiceInterface) *jwksHandler {
return &jwksHandler{
jwksService: jwksService,
}
}

// HandleJWKSRequest handles the HTTP request to retrieve the JSON Web Key Set (JWKS).
func (h *JWKSHandler) HandleJWKSRequest(w http.ResponseWriter, r *http.Request) {
func (h *jwksHandler) HandleJWKSRequest(w http.ResponseWriter, r *http.Request) {
logger := log.GetLogger().With(log.String(log.LoggerKeyComponentName, "JWKSHandler"))

jwksResponse, svcErr := h.jwksService.GetJWKS()
Expand All @@ -64,7 +62,7 @@ func (h *JWKSHandler) HandleJWKSRequest(w http.ResponseWriter, r *http.Request)
}

// handleError handles errors by writing an appropriate error response to the HTTP response writer.
func (h *JWKSHandler) handleError(w http.ResponseWriter, logger *log.Logger,
func (h *jwksHandler) handleError(w http.ResponseWriter, logger *log.Logger,
svcErr *serviceerror.ServiceError) {
w.Header().Set(serverconst.ContentTypeHeaderName, serverconst.ContentTypeJSON)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,32 @@
* under the License.
*/

package services
package jwks

import (
"net/http"

"github.com/asgardeo/thunder/internal/oauth/jwks/handler"
"github.com/asgardeo/thunder/internal/system/middleware"
)

// JWKSAPIService defines the API service for handling JWKS requests.
type JWKSAPIService struct {
jwksHandler *handler.JWKSHandler
// Initialize initializes the JWKS service and registers its routes.
func Initialize(mux *http.ServeMux) JWKSServiceInterface {
// Initialize the JWKS service
jwksService := newJWKSService()
jwksHandler := newJWKSHandler(jwksService)
registerRoutes(mux, jwksHandler)
return jwksService
}

// NewJWKSAPIService creates a new instance of JWKSAPIService.
func NewJWKSAPIService(mux *http.ServeMux) ServiceInterface {
instance := &JWKSAPIService{
jwksHandler: handler.NewJWKSHandler(),
}
instance.RegisterRoutes(mux)

return instance
}

// RegisterRoutes registers the routes for the JWKSAPIService.
func (s *JWKSAPIService) RegisterRoutes(mux *http.ServeMux) {
// registerRoutes registers the routes for the JWKSAPIService.
func registerRoutes(mux *http.ServeMux, jwksHandler *jwksHandler) {
opts := middleware.CORSOptions{
AllowedMethods: "GET, OPTIONS",
AllowedHeaders: "Content-Type, Authorization",
AllowCredentials: true,
}
mux.HandleFunc(middleware.WithCORS("GET /oauth2/jwks",
s.jwksHandler.HandleJWKSRequest, opts))
jwksHandler.HandleJWKSRequest, opts))
mux.HandleFunc(middleware.WithCORS("OPTIONS /oauth2/jwks",
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
* under the License.
*/

// Package model defines the data structures used in the JWKS service.
package model
package jwks

// JWKS defines the structure of a JSON Web Key Set.
type JWKS struct {
Expand Down
41 changes: 17 additions & 24 deletions backend/internal/oauth/jwks/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,52 +29,45 @@ import (
// Use crypto/sha1 only for JWKS x5t as required by spec for thumbprint.
"crypto/sha1" //nolint:gosec

"github.com/asgardeo/thunder/internal/cert"
"github.com/asgardeo/thunder/internal/oauth/jwks/constants"
"github.com/asgardeo/thunder/internal/oauth/jwks/model"
"github.com/asgardeo/thunder/internal/system/config"
"github.com/asgardeo/thunder/internal/system/crypto/hash"
"github.com/asgardeo/thunder/internal/system/error/serviceerror"
)

// JWKSServiceInterface defines the interface for JWKS service.
type JWKSServiceInterface interface {
GetJWKS() (*model.JWKSResponse, *serviceerror.ServiceError)
GetJWKS() (*JWKSResponse, *serviceerror.ServiceError)
}

// JWKSService implements the JWKSServiceInterface.
type JWKSService struct {
SystemCertService cert.SystemCertificateServiceInterface
}
// jwksService implements the JWKSServiceInterface.
type jwksService struct{}

// NewJWKSService creates a new instance of JWKSService.
func NewJWKSService() JWKSServiceInterface {
return &JWKSService{
SystemCertService: cert.NewSystemCertificateService(),
}
// newJWKSService creates a new instance of JWKSService.
func newJWKSService() JWKSServiceInterface {
return &jwksService{}
}

// GetJWKS retrieves the JSON Web Key Set (JWKS) from the server's TLS certificate.
func (s *JWKSService) GetJWKS() (*model.JWKSResponse, *serviceerror.ServiceError) {
func (s *jwksService) GetJWKS() (*JWKSResponse, *serviceerror.ServiceError) {
certConfig := config.GetThunderRuntime().CertConfig

kid := certConfig.CertKid
if kid == "" {
return nil, constants.ErrorCertificateKidNotFound
return nil, ErrorCertificateKidNotFound
}

tlsConfig := certConfig.TLSConfig
if tlsConfig == nil {
return nil, constants.ErrorTLSConfigNotFound
return nil, ErrorTLSConfigNotFound
}
if len(tlsConfig.Certificates) == 0 || len(tlsConfig.Certificates[0].Certificate) == 0 {
return nil, constants.ErrorNoCertificateFound
return nil, ErrorNoCertificateFound
}

certData := tlsConfig.Certificates[0].Certificate[0]
parsedCert, err := x509.ParseCertificate(certData)
if err != nil {
svcErr := constants.ErrorWhileParsingCertificate
svcErr := ErrorWhileParsingCertificate
svcErr.ErrorDescription = err.Error()
return nil, svcErr
}
Expand All @@ -87,7 +80,7 @@ func (s *JWKSService) GetJWKS() (*model.JWKSResponse, *serviceerror.ServiceError
x5t := base64.StdEncoding.EncodeToString(sha1Sum[:])
x5tS256 := hash.GenerateThumbprint(parsedCert.Raw)

var jwks model.JWKS
var jwks JWKS
switch pub := parsedCert.PublicKey.(type) {
case *rsa.PublicKey:
encodeBase64URL := func(b []byte) string {
Expand All @@ -107,7 +100,7 @@ func (s *JWKSService) GetJWKS() (*model.JWKSResponse, *serviceerror.ServiceError
}
eEnc := encodeBase64URL(eBytes)

jwks = model.JWKS{
jwks = JWKS{
Kid: kid,
Kty: "RSA",
Use: "sig",
Expand Down Expand Up @@ -135,7 +128,7 @@ func (s *JWKSService) GetJWKS() (*model.JWKSResponse, *serviceerror.ServiceError
alg = "ES512"
}

jwks = model.JWKS{
jwks = JWKS{
Kid: kid,
Kty: "EC",
Use: "sig",
Expand All @@ -148,10 +141,10 @@ func (s *JWKSService) GetJWKS() (*model.JWKSResponse, *serviceerror.ServiceError
X5tS256: x5tS256,
}
default:
return nil, constants.ErrorUnsupportedPublicKeyType
return nil, ErrorUnsupportedPublicKeyType
}

return &model.JWKSResponse{
Keys: []model.JWKS{jwks},
return &JWKSResponse{
Keys: []JWKS{jwks},
}, nil
}
Loading
Loading