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)
|
||||
|
||||
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")
|
||||
c.JSON(400, gin.H{
|
||||
"error": "invalid_grant",
|
||||
@@ -334,6 +346,9 @@ func (controller *OIDCController) Token(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// mark code as used to prevent reuse
|
||||
controller.oidc.MarkCodeAsUsed(controller.oidc.Hash(req.Code), entry.Userinfo.Sub)
|
||||
|
||||
if entry.RedirectURI != req.RedirectURI {
|
||||
controller.log.App.Warn().Msg("Redirect URI does not match")
|
||||
c.JSON(400, gin.H{
|
||||
|
||||
@@ -126,6 +126,10 @@ type AuthorizeCodeEntry struct {
|
||||
Userinfo UserinfoResponse
|
||||
}
|
||||
|
||||
type UsedCodeEntry struct {
|
||||
Sub string
|
||||
}
|
||||
|
||||
type OIDCService struct {
|
||||
log *logger.Logger
|
||||
config model.Config
|
||||
@@ -138,7 +142,8 @@ type OIDCService struct {
|
||||
issuer string
|
||||
|
||||
caches struct {
|
||||
code *CacheStore[AuthorizeCodeEntry]
|
||||
code *CacheStore[AuthorizeCodeEntry]
|
||||
usedCode *CacheStore[UsedCodeEntry]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +310,9 @@ func NewOIDCService(
|
||||
|
||||
// Create caches
|
||||
codeCash := NewCacheStore[AuthorizeCodeEntry](256)
|
||||
usedCode := NewCacheStore[UsedCodeEntry](256)
|
||||
service.caches.code = codeCash
|
||||
service.caches.usedCode = usedCode
|
||||
|
||||
// Start cache cleanup routine
|
||||
dg.Go(func(ctx context.Context) {
|
||||
@@ -316,6 +323,7 @@ func NewOIDCService(
|
||||
select {
|
||||
case <-ticker.C:
|
||||
service.caches.code.Sweep()
|
||||
service.caches.usedCode.Sweep()
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
@@ -406,7 +414,7 @@ func (service *OIDCService) CreateCode(req AuthorizeRequest, userContext model.U
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -817,3 +825,24 @@ func (service *OIDCService) hashAndEncodePKCE(codeVerifier string) string {
|
||||
func (service *OIDCService) CreateSub(userContext model.UserContext, clientId string) string {
|
||||
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