Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement OIDC auth #98

Merged
merged 4 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
implement OIDC auth
  • Loading branch information
Maronato committed Sep 15, 2023
commit 1dcb900cf31a2ae10edb6d47b3c4a478b0a083a8
25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -113,8 +113,8 @@ You would only need to specify the configuration options you want to change —
<details>
<summary>Configuration option list</summary>

| 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. |
Expand All @@ -136,6 +136,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. |

</details>

Expand Down Expand Up @@ -224,7 +227,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.

<details>
<summary>Integrate Github</summary>
Expand Down Expand Up @@ -252,6 +255,20 @@ Opengist can be configured to use OAuth to authenticate users, with GitHub or Gi
```
</details>

<details>
<summary>Integrate OpenID</summary>

* Add a new OAuth app in Application settings of your OIDC provider
* Set 'Redirect URI' to `http:https://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: <key>
oidc.secret: <secret>
# Discovery endpoint of the OpenID provider
oidc.url: http:https://auth.example.com/.well-known/openid-configuration
```
</details>

## License

Opengist is licensed under the [AGPL-3.0 license](LICENSE).
8 changes: 7 additions & 1 deletion config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ ssh.keygen-executable: ssh-keygen


# OAuth2 configuration
# The callback/redirect URL must be http:https://opengist.domain/oauth/<github|gitea>/callback
# The callback/redirect URL must be http:https://opengist.domain/oauth/<github|gitea|openid-connect>/callback

# To create a new OAuth2 application using GitHub : https://github.com/settings/applications/new
github.client-key:
Expand All @@ -71,3 +71,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:
8 changes: 8 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,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) {
Expand Down Expand Up @@ -237,5 +241,9 @@ func checks(c *config) error {
return err
}

if _, err := url.Parse(c.OIDCDiscoveryUrl); err != nil {
return err
}

return nil
}
8 changes: 8 additions & 0 deletions internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
32 changes: 31 additions & 1 deletion internal/web/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/models"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand All @@ -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)
}

Expand Down
1 change: 1 addition & 0 deletions internal/web/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,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)
}
Expand Down
3 changes: 3 additions & 0 deletions templates/pages/admin_config.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
<dt>Gitea client Key</dt><dd>{{ .c.GiteaClientKey }}</dd>
<dt>Gitea Secret</dt><dd>{{ .c.GiteaSecret }}</dd>
<dt>Gitea URL</dt><dd>{{ .c.GiteaUrl }}</dd>
<dt>OIDC client Key</dt><dd>{{ .c.OIDCClientKey }}</dd>
<dt>OIDC Secret</dt><dd>{{ .c.OIDCSecret }}</dd>
<dt>OIDC Discovery URL</dt><dd>{{ .c.OIDCDiscoveryUrl }}</dd>
</dl>
</div>
<div>
Expand Down
7 changes: 6 additions & 1 deletion templates/pages/auth_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ <h1 class="text-2xl font-bold leading-tight text-slate-700 dark:text-slate-300">
{{ .csrfHtml }}
</form>
{{ end }}
{{ if or .githubOauth .giteaOauth }}
{{ if or .githubOauth .giteaOauth .oidcOauth }}
{{ if not .disableForm }}
<div class="relative my-4">
<div class="absolute inset-0 flex items-center" aria-hidden="true">
Expand All @@ -71,6 +71,11 @@ <h1 class="text-2xl font-bold leading-tight text-slate-700 dark:text-slate-300">
Continue with Gitea account
</a>
{{ end }}
{{ if .oidcOauth }}
<a href="{{ $.c.ExternalUrl }}/oauth/openid-connect" class="block w-full mb-2 text-center whitespace-nowrap text-slate-700 dark:text-slate-300{{ if .syncReposFromFS }} text-slate-500 cursor-not-allowed {{ end }}rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2.5 py-2 text-xs font-medium text-gray-700 dark:text-white shadow-sm hover:bg-gray-100 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:outline-none focus:ring-1 focus:border-primary-500 focus:ring-primary-500 leading-3">
Continue with OpenID account
</a>
{{ end }}
</div>
{{ end }}
</div>
Expand Down
16 changes: 14 additions & 2 deletions templates/pages/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ <h1 class="text-2xl font-bold leading-tight">Settings</h1>
</header>
<main>
<div class="space-y-4">
<div class="sm:grid {{ if or .githubOauth .giteaOauth }}grid-cols-3{{else}}grid-cols-2{{end}} gap-x-4 md:gap-x-8">
<div class="sm:grid {{ if or .githubOauth .giteaOauth .oidcOauth }}grid-cols-3{{else}}grid-cols-2{{end}} gap-x-4 md:gap-x-8">
<div class="w-full">
<div class="bg-white dark:bg-gray-900 rounded-md border border-1 border-gray-200 dark:border-gray-700 py-8 px-4 shadow sm:rounded-lg sm:px-10">
<h2 class="text-md font-bold text-slate-700 dark:text-slate-300">
Expand All @@ -27,7 +27,7 @@ <h3 class="text-sm text-gray-600 dark:text-gray-400 italic mb-4">
</form>
</div>
</div>
{{ if or .githubOauth .giteaOauth }}
{{ if or .githubOauth .giteaOauth .oidcOauth }}
<div class="w-full">
<div class="bg-white dark:bg-gray-900 rounded-md border border-1 border-gray-200 dark:border-gray-700 py-8 px-4 shadow sm:rounded-lg sm:px-10">
<h2 class="text-md font-bold text-slate-700 dark:text-slate-300 mb-2">
Expand Down Expand Up @@ -60,6 +60,18 @@ <h2 class="text-md font-bold text-slate-700 dark:text-slate-300 mb-2">
</a>
{{ end }}
{{ end }}
{{ if .oidcOauth }}
{{ if .userLogged.OIDCID }}
<a href="{{ $.c.ExternalUrl }}/oauth/openid-connect" class="block w-full text-center whitespace-nowrap text-slate-700 dark:text-slate-300{{ if .syncReposFromFS }} text-slate-500 cursor-not-allowed {{ end }}rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2.5 py-2 text-xs font-medium text-gray-700 dark:text-white shadow-sm hover:bg-gray-200 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:outline-none focus:ring-1 focus:border-primary-500 focus:ring-primary-500 leading-3"
onclick="return confirm('Are you sure you want to unlink your OpenID account? You may lose access to Opengist if it\'s your only way to log in.')">
Unlink OpenID account
</a>
{{ else }}
<a href="{{ $.c.ExternalUrl }}/oauth/openid-connect" class="block w-full text-center whitespace-nowrap text-slate-700 dark:text-slate-300{{ if .syncReposFromFS }} text-slate-500 cursor-not-allowed {{ end }}rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2.5 py-2 text-xs font-medium text-gray-700 dark:text-white shadow-sm hover:bg-gray-200 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:outline-none focus:ring-1 focus:border-primary-500 focus:ring-primary-500 leading-3">
Link OpenID account
</a>
{{ end }}
{{ end }}

</div>
</div>
Expand Down