mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-06-02 17:40:14 +00:00
fix: ensure no oidc code reuse
This commit is contained in:
@@ -327,6 +327,18 @@ func (controller *OIDCController) Token(c *gin.Context) {
|
|||||||
entry, ok := controller.oidc.GetCodeEntry(controller.oidc.Hash(req.Code), client.ClientID)
|
entry, ok := controller.oidc.GetCodeEntry(controller.oidc.Hash(req.Code), client.ClientID)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
|
// ensure no code reuse
|
||||||
|
usedCodeSub, ok := controller.oidc.IsCodeUsed(controller.oidc.Hash(req.Code))
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
controller.log.App.Warn().Msg("Code reuse detected")
|
||||||
|
controller.oidc.DeleteSessionBySub(c, usedCodeSub)
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"error": "invalid_grant",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
controller.log.App.Warn().Msg("Code not found")
|
controller.log.App.Warn().Msg("Code not found")
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"error": "invalid_grant",
|
"error": "invalid_grant",
|
||||||
@@ -334,6 +346,9 @@ func (controller *OIDCController) Token(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mark code as used to prevent reuse
|
||||||
|
controller.oidc.MarkCodeAsUsed(controller.oidc.Hash(req.Code), entry.Userinfo.Sub)
|
||||||
|
|
||||||
if entry.RedirectURI != req.RedirectURI {
|
if entry.RedirectURI != req.RedirectURI {
|
||||||
controller.log.App.Warn().Msg("Redirect URI does not match")
|
controller.log.App.Warn().Msg("Redirect URI does not match")
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
|
|||||||
@@ -126,6 +126,10 @@ type AuthorizeCodeEntry struct {
|
|||||||
Userinfo UserinfoResponse
|
Userinfo UserinfoResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UsedCodeEntry struct {
|
||||||
|
Sub string
|
||||||
|
}
|
||||||
|
|
||||||
type OIDCService struct {
|
type OIDCService struct {
|
||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
config model.Config
|
config model.Config
|
||||||
@@ -139,6 +143,7 @@ type OIDCService struct {
|
|||||||
|
|
||||||
caches struct {
|
caches struct {
|
||||||
code *CacheStore[AuthorizeCodeEntry]
|
code *CacheStore[AuthorizeCodeEntry]
|
||||||
|
usedCode *CacheStore[UsedCodeEntry]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +310,9 @@ func NewOIDCService(
|
|||||||
|
|
||||||
// Create caches
|
// Create caches
|
||||||
codeCash := NewCacheStore[AuthorizeCodeEntry](256)
|
codeCash := NewCacheStore[AuthorizeCodeEntry](256)
|
||||||
|
usedCode := NewCacheStore[UsedCodeEntry](256)
|
||||||
service.caches.code = codeCash
|
service.caches.code = codeCash
|
||||||
|
service.caches.usedCode = usedCode
|
||||||
|
|
||||||
// Start cache cleanup routine
|
// Start cache cleanup routine
|
||||||
dg.Go(func(ctx context.Context) {
|
dg.Go(func(ctx context.Context) {
|
||||||
@@ -316,6 +323,7 @@ func NewOIDCService(
|
|||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
service.caches.code.Sweep()
|
service.caches.code.Sweep()
|
||||||
|
service.caches.usedCode.Sweep()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -406,7 +414,7 @@ func (service *OIDCService) CreateCode(req AuthorizeRequest, userContext model.U
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store the code in the cache
|
// Store the code in the cache
|
||||||
service.caches.code.Set(entry.CodeHash, entry, 10*time.Minute)
|
service.caches.code.Set(entry.CodeHash, entry, 1*time.Minute)
|
||||||
|
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
@@ -817,3 +825,24 @@ func (service *OIDCService) hashAndEncodePKCE(codeVerifier string) string {
|
|||||||
func (service *OIDCService) CreateSub(userContext model.UserContext, clientId string) string {
|
func (service *OIDCService) CreateSub(userContext model.UserContext, clientId string) string {
|
||||||
return utils.GenerateUUID(fmt.Sprintf("%s:%s", userContext.GetUsername(), clientId))
|
return utils.GenerateUUID(fmt.Sprintf("%s:%s", userContext.GetUsername(), clientId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service *OIDCService) IsCodeUsed(codeHash string) (string, bool) {
|
||||||
|
entry, ok := service.caches.usedCode.Get(codeHash)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.Sub, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *OIDCService) MarkCodeAsUsed(codeHash string, sub string) {
|
||||||
|
entry := UsedCodeEntry{
|
||||||
|
Sub: sub,
|
||||||
|
}
|
||||||
|
service.caches.usedCode.Set(codeHash, entry, 2*time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *OIDCService) DeleteSessionBySub(ctx context.Context, sub string) error {
|
||||||
|
return service.queries.DeleteOIDCSessionBySub(ctx, sub)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user