mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-05-16 17:20:14 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5349f21212 | |||
| e8071a9d80 | |||
| 1f67797605 | |||
| ca06099466 | |||
| d4b4245017 | |||
| 4c741a5990 |
@@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help improve Tinyauth
|
|
||||||
title: "[BUG]"
|
|
||||||
labels: bug
|
|
||||||
assignees:
|
|
||||||
- steveiliop56
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Logs**
|
|
||||||
Please include the Tinyauth logs below, make sure to not include sensitive info.
|
|
||||||
|
|
||||||
**Device (please complete the following information):**
|
|
||||||
- OS: [e.g. iOS]
|
|
||||||
- Browser [e.g. chrome, safari]
|
|
||||||
- Tinyauth [e.g. v2.1.1]
|
|
||||||
- Docker [e.g. 27.3.1]
|
|
||||||
|
|
||||||
**
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Create a report to help us improve this project
|
||||||
|
title: "[BUG]"
|
||||||
|
labels: bug
|
||||||
|
assignees:
|
||||||
|
- steveiliop56
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for reporting a bug! Please provide detailed information below.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Describe the Bug
|
||||||
|
description: "A clear and concise description of what the bug is."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduce
|
||||||
|
attributes:
|
||||||
|
label: How to Reproduce
|
||||||
|
description: Steps to reproduce the behavior.
|
||||||
|
value: |
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: "A clear and concise description of what you expected to happen."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: "Additional Context"
|
||||||
|
description: "If applicable add screenshots to help explain your problem."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: "Logs"
|
||||||
|
description: "Please include the Tinyauth logs, make sure to not include sensitive info."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
placeholder: "e.g. iOS, android, windows, linux, etc"
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: browser
|
||||||
|
attributes:
|
||||||
|
label: Browser
|
||||||
|
placeholder: "e.g. chrome, firefox, safari, edge, etc"
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: tinyauth
|
||||||
|
attributes:
|
||||||
|
label: Tinyauth Version
|
||||||
|
placeholder: "e.g. v5.0.0"
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: docker
|
||||||
|
attributes:
|
||||||
|
label: Docker Version (if applicable)
|
||||||
|
placeholder: "e.g. 27.3.1"
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: not-llm
|
||||||
|
attributes:
|
||||||
|
label: Human Written Confirmation
|
||||||
|
options:
|
||||||
|
- label: I confirm this issue was written by me and not generated by an LLM or AI assistant.
|
||||||
|
required: true
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Tinyauth Community Support on Discord
|
||||||
|
url: https://discord.gg/eHzVaCzRRd
|
||||||
|
about: Please ask and answer questions here.
|
||||||
|
- name: Tinyauth Documentation
|
||||||
|
url: https://tinyauth.app/docs/getting-started/
|
||||||
|
about: Please check the documentation here.
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: "[FEATURE]"
|
|
||||||
labels: enhancement
|
|
||||||
assignees:
|
|
||||||
- steveiliop56
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: "[FEATURE]"
|
||||||
|
labels: enhancement
|
||||||
|
assignees:
|
||||||
|
- steveiliop56
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for suggesting a feature! Please provide detailed information below.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: Is your feature request related to a problem? Please describe.
|
||||||
|
description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Describe the solution you'd like.
|
||||||
|
description: "A clear and concise description of what you want to happen."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Describe alternatives you've considered.
|
||||||
|
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: "Add any other context or screenshots about the feature request here."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: not-llm
|
||||||
|
attributes:
|
||||||
|
label: Human Written Confirmation
|
||||||
|
options:
|
||||||
|
- label: I confirm this request was written by me and not generated by an LLM or AI assistant.
|
||||||
|
required: true
|
||||||
@@ -208,7 +208,12 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
|||||||
name = user.Name
|
name = user.Name
|
||||||
} else {
|
} else {
|
||||||
controller.log.App.Debug().Msg("No name from OAuth provider, generating from email")
|
controller.log.App.Debug().Msg("No name from OAuth provider, generating from email")
|
||||||
name = fmt.Sprintf("%s (%s)", utils.Capitalize(strings.Split(user.Email, "@")[0]), strings.Split(user.Email, "@")[1])
|
parts := strings.SplitN(user.Email, "@", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
name = fmt.Sprintf("%s (%s)", utils.Capitalize(parts[0]), parts[1])
|
||||||
|
} else {
|
||||||
|
name = utils.Capitalize(user.Email)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var username string
|
var username string
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ func (controller *OIDCController) Authorize(c *gin.Context) {
|
|||||||
client, ok := controller.oidc.GetClient(req.ClientID)
|
client, ok := controller.oidc.GetClient(req.ClientID)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
controller.authorizeError(c, err, "Client not found", "The client ID is invalid", "", "", "")
|
controller.authorizeError(c, fmt.Errorf("client not found: %s", req.ClientID), "Client not found", "The client ID is invalid", "", "", "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ func (controller *OIDCController) Token(c *gin.Context) {
|
|||||||
entry, err := controller.oidc.GetCodeEntry(c, controller.oidc.Hash(req.Code), client.ClientID)
|
entry, err := controller.oidc.GetCodeEntry(c, controller.oidc.Hash(req.Code), client.ClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err := controller.oidc.DeleteTokenByCodeHash(c, controller.oidc.Hash(req.Code)); err != nil {
|
if err := controller.oidc.DeleteTokenByCodeHash(c, controller.oidc.Hash(req.Code)); err != nil {
|
||||||
controller.log.App.Error().Err(err).Msg("Failed to delete code")
|
controller.log.App.Error().Err(err).Msg("Failed to revoke tokens for replayed code")
|
||||||
}
|
}
|
||||||
if errors.Is(err, service.ErrCodeNotFound) {
|
if errors.Is(err, service.ErrCodeNotFound) {
|
||||||
controller.log.App.Warn().Msg("Code not found")
|
controller.log.App.Warn().Msg("Code not found")
|
||||||
|
|||||||
@@ -144,9 +144,9 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
|||||||
|
|
||||||
if !controller.useBrowserResponse(proxyCtx) {
|
if !controller.useBrowserResponse(proxyCtx) {
|
||||||
c.Header("x-tinyauth-location", redirectURL)
|
c.Header("x-tinyauth-location", redirectURL)
|
||||||
c.JSON(401, gin.H{
|
c.JSON(403, gin.H{
|
||||||
"status": 401,
|
"status": 403,
|
||||||
"message": "Unauthorized",
|
"message": "Forbidden",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (controller *ResourcesController) resourcesHandler(c *gin.Context) {
|
|||||||
if controller.config.Resources.Path == "" {
|
if controller.config.Resources.Path == "" {
|
||||||
c.JSON(404, gin.H{
|
c.JSON(404, gin.H{
|
||||||
"status": 404,
|
"status": 404,
|
||||||
"message": "Resources not found",
|
"message": "Resource not found",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -773,46 +773,49 @@ func (auth *AuthService) ensureOAuthSessionLimit() {
|
|||||||
auth.oauthMutex.Lock()
|
auth.oauthMutex.Lock()
|
||||||
defer auth.oauthMutex.Unlock()
|
defer auth.oauthMutex.Unlock()
|
||||||
|
|
||||||
if len(auth.oauthPendingSessions) >= MaxOAuthPendingSessions {
|
if len(auth.oauthPendingSessions) <= MaxOAuthPendingSessions {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cleanupIds := make([]string, 0, OAuthCleanupCount)
|
type entry struct {
|
||||||
|
id string
|
||||||
|
expiresAt int64
|
||||||
|
}
|
||||||
|
|
||||||
for range OAuthCleanupCount {
|
entries := make([]entry, 0, len(auth.oauthPendingSessions))
|
||||||
oldestId := ""
|
for id, session := range auth.oauthPendingSessions {
|
||||||
oldestTime := int64(0)
|
entries = append(entries, entry{id, session.ExpiresAt.Unix()})
|
||||||
|
}
|
||||||
|
|
||||||
for id, session := range auth.oauthPendingSessions {
|
slices.SortFunc(entries, func(a, b entry) int {
|
||||||
if oldestTime == 0 {
|
if a.expiresAt < b.expiresAt {
|
||||||
oldestId = id
|
return -1
|
||||||
oldestTime = session.ExpiresAt.Unix()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if slices.Contains(cleanupIds, id) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if session.ExpiresAt.Unix() < oldestTime {
|
|
||||||
oldestId = id
|
|
||||||
oldestTime = session.ExpiresAt.Unix()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanupIds = append(cleanupIds, oldestId)
|
|
||||||
}
|
}
|
||||||
|
if a.expiresAt > b.expiresAt {
|
||||||
for _, id := range cleanupIds {
|
return 1
|
||||||
delete(auth.oauthPendingSessions, id)
|
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, e := range entries[:OAuthCleanupCount] {
|
||||||
|
delete(auth.oauthPendingSessions, e.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthService) lockdownMode() {
|
func (auth *AuthService) lockdownMode() {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
|
||||||
auth.lockdownCtx = ctx
|
|
||||||
auth.lockdownCancelFunc = cancel
|
|
||||||
|
|
||||||
auth.loginMutex.Lock()
|
auth.loginMutex.Lock()
|
||||||
|
|
||||||
|
if auth.lockdown != nil && auth.lockdown.Active {
|
||||||
|
auth.loginMutex.Unlock()
|
||||||
|
cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
auth.lockdownCtx = ctx
|
||||||
|
auth.lockdownCancelFunc = cancel
|
||||||
|
|
||||||
auth.log.App.Warn().Msg("Too many failed login attempts, entering lockdown mode")
|
auth.log.App.Warn().Msg("Too many failed login attempts, entering lockdown mode")
|
||||||
|
|
||||||
auth.lockdown = &Lockdown{
|
auth.lockdown = &Lockdown{
|
||||||
@@ -825,10 +828,12 @@ func (auth *AuthService) lockdownMode() {
|
|||||||
auth.loginAttempts = make(map[string]*LoginAttempt)
|
auth.loginAttempts = make(map[string]*LoginAttempt)
|
||||||
|
|
||||||
timer := time.NewTimer(time.Until(auth.lockdown.ActiveUntil))
|
timer := time.NewTimer(time.Until(auth.lockdown.ActiveUntil))
|
||||||
defer timer.Stop()
|
|
||||||
|
|
||||||
auth.loginMutex.Unlock()
|
auth.loginMutex.Unlock()
|
||||||
|
|
||||||
|
defer cancel()
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
// Timer expired, end lockdown
|
// Timer expired, end lockdown
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func NewOAuthService(config model.OAuthServiceConfig, id string, ctx context.Con
|
|||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: config.Insecure,
|
InsecureSkipVerify: config.Insecure,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ type OIDCService struct {
|
|||||||
|
|
||||||
clients map[string]model.OIDCClientConfig
|
clients map[string]model.OIDCClientConfig
|
||||||
privateKey *rsa.PrivateKey
|
privateKey *rsa.PrivateKey
|
||||||
publicKey crypto.PublicKey
|
publicKey *rsa.PublicKey
|
||||||
issuer string
|
issuer string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ func NewOIDCService(
|
|||||||
|
|
||||||
clients: clients,
|
clients: clients,
|
||||||
privateKey: privateKey,
|
privateKey: privateKey,
|
||||||
publicKey: publicKey,
|
publicKey: publicKey.(*rsa.PublicKey),
|
||||||
issuer: issuer,
|
issuer: issuer,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +296,7 @@ func (service *OIDCService) ValidateAuthorizeParams(req AuthorizeRequest) error
|
|||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("access_denied")
|
return errors.New("access_denied")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect URI to verify that it's trusted
|
// Redirect URI to verify that it's trusted
|
||||||
if !slices.Contains(client.TrustedRedirectURIs, req.RedirectURI) {
|
if !slices.Contains(client.TrustedRedirectURIs, req.RedirectURI) {
|
||||||
return errors.New("invalid_request_uri")
|
return errors.New("invalid_request_uri")
|
||||||
@@ -455,7 +455,7 @@ func (service *OIDCService) generateIDToken(client model.OIDCClientConfig, user
|
|||||||
|
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
|
|
||||||
der := x509.MarshalPKCS1PublicKey(&service.privateKey.PublicKey)
|
der := x509.MarshalPKCS1PublicKey(service.publicKey)
|
||||||
|
|
||||||
if der == nil {
|
if der == nil {
|
||||||
return "", errors.New("failed to marshal public key")
|
return "", errors.New("failed to marshal public key")
|
||||||
@@ -813,7 +813,7 @@ func (service *OIDCService) cleanupRoutine() {
|
|||||||
func (service *OIDCService) GetJWK() ([]byte, error) {
|
func (service *OIDCService) GetJWK() ([]byte, error) {
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
|
|
||||||
der := x509.MarshalPKCS1PublicKey(&service.privateKey.PublicKey)
|
der := x509.MarshalPKCS1PublicKey(service.publicKey)
|
||||||
|
|
||||||
if der == nil {
|
if der == nil {
|
||||||
return nil, errors.New("failed to marshal public key")
|
return nil, errors.New("failed to marshal public key")
|
||||||
|
|||||||
Reference in New Issue
Block a user