diff --git a/README.md b/README.md index 25c3ae3..9d705e2 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A self-hosted pastebin **powered by Git**. [Try it here](https://opengist.thomic * Search for snippets ; browse users snippets, likes and forks * Editor with indentation mode & size ; drag and drop files * Download raw files or as a ZIP archive -* OAuth2 login with GitHub and Gitea +* OAuth2 login with GitHub, Gitea, and OpenID Connect * Avatars via Gravatar or OAuth2 providers * Light/Dark mode * Responsive UI @@ -113,8 +113,8 @@ You would only need to specify the configuration options you want to change —
Configuration option list -| YAML Config Key | Environment Variable | Default value | Description | -|-----------------------|--------------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| YAML Config Key | Environment Variable | Default value | Description | +| --------------------- | ------------------------ | -------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | log-level | OG_LOG_LEVEL | `warn` | Set the log level to one of the following: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `panic`. | | external-url | OG_EXTERNAL_URL | none | Public URL for the Git HTTP/SSH connection. If not set, uses the URL from the request. | | opengist-home | OG_OPENGIST_HOME | home directory | Path to the directory where Opengist stores its data. | @@ -133,6 +133,9 @@ You would only need to specify the configuration options you want to change — | gitea.client-key | OG_GITEA_CLIENT_KEY | none | The client key for the Gitea OAuth application. | | gitea.secret | OG_GITEA_SECRET | none | The secret for the Gitea OAuth application. | | gitea.url | OG_GITEA_URL | `https://gitea.com/` | The URL of the Gitea instance. | +| oidc.client-key | OG_OIDC_CLIENT_KEY | none | The client key for the OpenID application. | +| oidc.secret | OG_OIDC_SECRET | none | The secret for the OpenID application. | +| oidc.discovery-url | OG_OIDC_DISCOVERY_URL | none | Discovery endpoint of the OpenID provider. |
@@ -221,7 +224,7 @@ service fail2ban restart ## Configure OAuth -Opengist can be configured to use OAuth to authenticate users, with GitHub or Gitea. +Opengist can be configured to use OAuth to authenticate users, with GitHub, Gitea, or OpenID Connect.
Integrate Github @@ -249,6 +252,20 @@ Opengist can be configured to use OAuth to authenticate users, with GitHub or Gi ```
+
+Integrate OpenID + +* Add a new OAuth app in Application settings of your OIDC provider +* Set 'Redirect URI' to `http://opengist.domain/oauth/openid-connect/callback` +* Copy the 'Client ID', 'Client Secret', and the discovery endpoint, and add them to the configuration : + ```yaml + oidc.client-key: + oidc.secret: + # Discovery endpoint of the OpenID provider + oidc.discovery-url: http://auth.example.com/.well-known/openid-configuration + ``` +
+ ## License Opengist is licensed under the [AGPL-3.0 license](LICENSE). diff --git a/config.yml b/config.yml index d0898b2..f549beb 100644 --- a/config.yml +++ b/config.yml @@ -51,7 +51,7 @@ ssh.keygen-executable: ssh-keygen # OAuth2 configuration -# The callback/redirect URL must be http://opengist.domain/oauth//callback +# The callback/redirect URL must be http://opengist.domain/oauth//callback # To create a new OAuth2 application using GitHub : https://github.com/settings/applications/new github.client-key: @@ -62,3 +62,9 @@ gitea.client-key: gitea.secret: # URL of the Gitea instance. Default: https://gitea.com/ gitea.url: https://gitea.com/ + +# To create a new OAuth2 application using OpenID Connect: +oidc.client-key: +oidc.secret: +# Discovery endpoint of the OpenID provider +oidc.discovery-url: diff --git a/internal/config/config.go b/internal/config/config.go index cc62056..56dfbb0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -45,6 +45,10 @@ type config struct { GiteaClientKey string `yaml:"gitea.client-key" env:"OG_GITEA_CLIENT_KEY"` GiteaSecret string `yaml:"gitea.secret" env:"OG_GITEA_SECRET"` GiteaUrl string `yaml:"gitea.url" env:"OG_GITEA_URL"` + + OIDCClientKey string `yaml:"oidc.client-key" env:"OG_OIDC_CLIENT_KEY"` + OIDCSecret string `yaml:"oidc.secret" env:"OG_OIDC_SECRET"` + OIDCDiscoveryUrl string `yaml:"oidc.discovery-url" env:"OG_OIDC_DISCOVERY_URL"` } func configWithDefaults() (*config, error) { @@ -222,5 +226,9 @@ func checks(c *config) error { return err } + if _, err := url.Parse(c.OIDCDiscoveryUrl); err != nil { + return err + } + return nil } diff --git a/internal/db/user.go b/internal/db/user.go index f73b89b..9c385e8 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -15,6 +15,7 @@ type User struct { AvatarURL string GithubID string GiteaID string + OIDCID string `gorm:"column:oidc_id"` Gists []Gist `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;foreignKey:UserID"` SSHKeys []SSHKey `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;foreignKey:UserID"` @@ -124,6 +125,8 @@ func GetUserByProvider(id string, provider string) (*User, error) { err = db.Where("github_id = ?", id).First(&user).Error case "gitea": err = db.Where("gitea_id = ?", id).First(&user).Error + case "openid-connect": + err = db.Where("oidc_id = ?", id).First(&user).Error } return user, err @@ -169,6 +172,11 @@ func (user *User) DeleteProviderID(provider string) error { Update("gitea_id", nil). Update("avatar_url", nil). Error + case "openid-connect": + return db.Model(&user). + Update("oidc_id", nil). + Update("avatar_url", nil). + Error } return nil diff --git a/internal/web/auth.go b/internal/web/auth.go index 4a1d37d..2457f22 100644 --- a/internal/web/auth.go +++ b/internal/web/auth.go @@ -16,6 +16,7 @@ import ( "github.com/markbates/goth/gothic" "github.com/markbates/goth/providers/gitea" "github.com/markbates/goth/providers/github" + "github.com/markbates/goth/providers/openidConnect" "github.com/rs/zerolog/log" "github.com/thomiceli/opengist/internal/config" "github.com/thomiceli/opengist/internal/db" @@ -150,6 +151,9 @@ func oauthCallback(ctx echo.Context) error { case "gitea": currUser.GiteaID = user.UserID currUser.AvatarURL = getAvatarUrlFromProvider("gitea", user.NickName) + case "openid-connect": + currUser.OIDCID = user.UserID + currUser.AvatarURL = user.AvatarURL } if err = currUser.Update(); err != nil { @@ -185,6 +189,9 @@ func oauthCallback(ctx echo.Context) error { case "gitea": userDB.GiteaID = user.UserID userDB.AvatarURL = getAvatarUrlFromProvider("gitea", user.NickName) + case "openid-connect": + userDB.OIDCID = user.UserID + userDB.AvatarURL = user.AvatarURL } if err = userDB.Create(); err != nil { @@ -208,6 +215,8 @@ func oauthCallback(ctx echo.Context) error { resp, err = http.Get("https://github.com/" + user.NickName + ".keys") case "gitea": resp, err = http.Get(urlJoin(config.C.GiteaUrl, user.NickName+".keys")) + case "openid-connect": + err = errors.New("cannot get keys from OIDC provider") } if err == nil { @@ -282,6 +291,22 @@ func oauth(ctx echo.Context) error { urlJoin(config.C.GiteaUrl, "/api/v1/user"), ), ) + case "openid-connect": + oidcProvider, err := openidConnect.New( + config.C.OIDCClientKey, + config.C.OIDCSecret, + urlJoin(opengistUrl, "/oauth/openid-connect/callback"), + config.C.OIDCDiscoveryUrl, + "openid", + "email", + "profile", + ) + + if err != nil { + return errorRes(500, "Cannot create OIDC provider", err) + } + + goth.UseProviders(oidcProvider) } currUser := getUserLogged(ctx) @@ -299,6 +324,11 @@ func oauth(ctx echo.Context) error { isDelete = true err = currUser.DeleteProviderID(provider) } + case "openid-connect": + if currUser.OIDCID != "" { + isDelete = true + err = currUser.DeleteProviderID(provider) + } } if err != nil { @@ -313,7 +343,7 @@ func oauth(ctx echo.Context) error { ctxValue := context.WithValue(ctx.Request().Context(), gothic.ProviderParamKey, provider) ctx.SetRequest(ctx.Request().WithContext(ctxValue)) - if provider != "github" && provider != "gitea" { + if provider != "github" && provider != "gitea" && provider != "openid-connect" { return errorRes(400, "Unsupported provider", nil) } diff --git a/internal/web/server.go b/internal/web/server.go index df29fbe..f95191b 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -307,6 +307,7 @@ func dataInit(next echo.HandlerFunc) echo.HandlerFunc { setData(ctx, "githubOauth", config.C.GithubClientKey != "" && config.C.GithubSecret != "") setData(ctx, "giteaOauth", config.C.GiteaClientKey != "" && config.C.GiteaSecret != "") + setData(ctx, "oidcOauth", config.C.OIDCClientKey != "" && config.C.OIDCSecret != "" && config.C.OIDCDiscoveryUrl != "") return next(ctx) } diff --git a/templates/pages/admin_config.html b/templates/pages/admin_config.html index 2234b92..0c98a87 100644 --- a/templates/pages/admin_config.html +++ b/templates/pages/admin_config.html @@ -55,6 +55,9 @@
Gitea client Key
{{ .c.GiteaClientKey }}
Gitea Secret
{{ .c.GiteaSecret }}
Gitea URL
{{ .c.GiteaUrl }}
+
OIDC client Key
{{ .c.OIDCClientKey }}
+
OIDC Secret
{{ .c.OIDCSecret }}
+
OIDC Discovery URL
{{ .c.OIDCDiscoveryUrl }}
diff --git a/templates/pages/auth_form.html b/templates/pages/auth_form.html index 0662fe7..64179f5 100644 --- a/templates/pages/auth_form.html +++ b/templates/pages/auth_form.html @@ -51,7 +51,7 @@ {{ .csrfHtml }} {{ end }} - {{ if or .githubOauth .giteaOauth }} + {{ if or .githubOauth .giteaOauth .oidcOauth }} {{ if not .disableForm }}
{{ end }}
diff --git a/templates/pages/settings.html b/templates/pages/settings.html index 605f449..0e10aed 100644 --- a/templates/pages/settings.html +++ b/templates/pages/settings.html @@ -7,7 +7,7 @@
-
+

@@ -27,7 +27,7 @@

- {{ if or .githubOauth .giteaOauth }} + {{ if or .githubOauth .giteaOauth .oidcOauth }}

@@ -60,6 +60,18 @@ {{ end }} {{ end }} + {{ if .oidcOauth }} + {{ if .userLogged.OIDCID }} + + Unlink OpenID account + + {{ else }} + + Link OpenID account + + {{ end }} + {{ end }}