feat: auth method oidc

This commit is contained in:
Marcell FÜLÖP 2023-02-19 11:39:06 +00:00
parent 1a5b999545
commit 1621683fdb
13 changed files with 266 additions and 7 deletions

143
auth/oidc.go Normal file
View File

@ -0,0 +1,143 @@
package auth
import (
"context"
"fmt"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users"
"log"
"math/rand"
"net/http"
"os"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
)
// MethodOIDCAuth is used to identify oidc auth.
const MethodOIDCAuth settings.AuthMethod = "oidc"
// OIDCAuth is an Open ID Connect auther implementation.
type OIDCAuth struct {
OIDC *OAuthClient `json:"oidc" yaml:"oidc"`
}
// Auth is requested by the identity provider in the callback phase of an oauth code flow.
func (a OIDCAuth) Auth(r *http.Request, usr users.Store, _ *settings.Settings, srv *settings.Server) (*users.User, error) {
cookie, _ := r.Cookie("auth")
if cookie != nil {
return nil, nil
}
log.Println("oidc auth callback")
u, err := a.OIDC.HandleAuthCallback(r, usr, srv)
return u, err
}
// LoginPage tells that oidc auth doesn't require a login page.
func (a OIDCAuth) LoginPage() bool {
return false
}
// OAuthClient describes the oidc connector parameters.
type OAuthClient struct {
ClientID string `json:"clientID"`
ClientSecret string `json:"clientSecret"`
Issuer string `json:"issuer"`
RedirectURL string `json:"redirectURL"`
OAuth2Config oauth2.Config `json:"-"`
Verifier *oidc.IDTokenVerifier `json:"-"`
}
// InitClient configures the connector via oidc discovery.
func (o *OAuthClient) InitClient() {
ctx := context.Background()
provider, err := oidc.NewProvider(ctx, o.Issuer)
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
o.Verifier = provider.Verifier(&oidc.Config{ClientID: o.ClientID})
o.OAuth2Config = oauth2.Config{
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
RedirectURL: o.RedirectURL,
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
}
// InitAuthFlow triggers oidc authentication flow
func (o *OAuthClient) InitAuthFlow(w http.ResponseWriter, r *http.Request) {
o.InitClient()
state := fmt.Sprintf("%x", rand.Uint32())
nonce := fmt.Sprintf("%x", rand.Uint32())
url := o.OAuth2Config.AuthCodeURL(state, oidc.Nonce(nonce))
log.Println("oidc init flow ", url)
w.Header().Set("Set-Cookie", "state="+state+"; path=/")
http.Redirect(w, r, url, http.StatusMovedPermanently)
}
// HandleAuthCallback manages code exchange and obtains the id token.
func (o *OAuthClient) HandleAuthCallback(r *http.Request, usr users.Store, srv *settings.Server) (*users.User, error) {
o.InitClient()
code := r.URL.Query().Get("code")
stateQuery := r.URL.Query().Get("state")
stateCookie, _ := r.Cookie("state")
if code == "" || stateQuery == "" || stateQuery != stateCookie.Value {
log.Fatal("Invalid request")
return nil, os.ErrInvalid
}
// Exchange code for token
oauth2Token, err := o.OAuth2Config.Exchange(context.Background(), code)
if err != nil {
log.Fatal(err)
return nil, err
}
log.Println("oidc got token")
// Parse id token
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
log.Fatal("Invalid token")
return nil, nil
}
log.Println("oidc parsed token")
// Verify id token
idToken, err := o.Verifier.Verify(context.Background(), rawIDToken)
if err != nil {
log.Fatal("oidc verify failed")
return nil, err
}
log.Println("oidc verified token")
// Extract claims
var claims struct {
Email string `json:"email"`
Verified bool `json:"email_verified"`
Username string `json:"preferred_username"`
Profile string `json:"profile"`
}
if err := idToken.Claims(&claims); err != nil {
log.Fatal(err)
return nil, err
}
// Find filebrowser user by oidc username
u, err := usr.Get(srv.Root, claims.Username)
if err != nil {
log.Println("oidc authenticated but no matching filebrowser user")
return nil, os.ErrPermission
}
u.AuthSource = "oidc"
log.Println("oidc success (user, claims) ", u, claims)
return u, nil
}

View File

@ -41,6 +41,11 @@ func addConfigFlags(flags *pflag.FlagSet) {
flags.String("recaptcha.key", "", "ReCaptcha site key")
flags.String("recaptcha.secret", "", "ReCaptcha secret")
flags.String("oidc.clientID", "", "Open ID Connect Client ID for auth.method=oidc")
flags.String("oidc.clientSecret", "", "Open ID Connect Client Secret for auth.method=oidc")
flags.String("oidc.issuer", "", "Open ID Connect Configuration Issuer auth.method=oidc")
flags.String("oidc.redirectURL", "", "Open ID Connect Redirect URL for auth.method=oidc")
flags.String("branding.name", "", "replace 'File Browser' by this name")
flags.String("branding.color", "", "set the theme color")
flags.String("branding.files", "", "path to directory with images and custom styles")
@ -116,6 +121,24 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.
auther = jsonAuth
}
if method == auth.MethodOIDCAuth {
oidcAuth := &auth.OIDCAuth{}
id := mustGetString(flags, "oidc.clientID")
secret := mustGetString(flags, "oidc.clientSecret")
url := mustGetString(flags, "oidc.issuer")
redirect := mustGetString(flags, "oidc.redirectURL")
if id != "" && secret != "" && url != "" && redirect != "" {
oidcAuth.OIDC = &auth.OAuthClient{
ClientID: id,
ClientSecret: secret,
Issuer: url,
RedirectURL: redirect,
}
}
auther = oidcAuth
}
if method == auth.MethodHookAuth {
command := mustGetString(flags, "auth.command")

View File

@ -66,6 +66,8 @@ The path must be for a json or yaml file.`,
switch file.Settings.AuthMethod {
case auth.MethodJSONAuth:
auther = getAuther(auth.JSONAuth{}, rawAuther).(*auth.JSONAuth)
case auth.MethodOIDCAuth:
auther = getAuther(auth.OIDCAuth{}, rawAuther).(*auth.OIDCAuth)
case auth.MethodNoAuth:
auther = getAuther(auth.NoAuth{}, rawAuther).(*auth.NoAuth)
case auth.MethodProxyAuth:

View File

@ -121,6 +121,7 @@ import {
disableUsedPercentage,
noAuth,
loginPage,
authMethod,
} from "@/utils/constants";
import { files as api } from "@/api";
import ProgressBar from "vue-simple-progress";
@ -141,7 +142,7 @@ export default {
version: () => version,
disableExternal: () => disableExternal,
disableUsedPercentage: () => disableUsedPercentage,
canLogout: () => !noAuth && loginPage,
canLogout: () => (!noAuth && loginPage) || authMethod === "oidc"
},
asyncComputed: {
usage: {

View File

@ -5,7 +5,7 @@ import store from "@/store";
import router from "@/router";
import i18n from "@/i18n";
import Vue from "@/utils/vue";
import { recaptcha, loginPage } from "@/utils/constants";
import { recaptcha, loginPage, authMethod } from "@/utils/constants";
import { login, validateLogin } from "@/utils/auth";
import App from "@/App";
@ -15,7 +15,7 @@ sync(store, router);
async function start() {
try {
if (loginPage) {
if (loginPage || authMethod === "oidc") {
await validateLogin();
} else {
await login("", "", "");

View File

@ -2,6 +2,7 @@ import store from "@/store";
import router from "@/router";
import { Base64 } from "js-base64";
import { baseURL } from "@/utils/constants";
import cookie from "@/utils/cookie";
export function parseToken(token) {
const parts = token.split(".");
@ -20,9 +21,15 @@ export function parseToken(token) {
}
export async function validateLogin() {
let jwt = localStorage.getItem("jwt")
if (!jwt || jwt === "null") {
jwt = cookie("auth");
}
try {
if (localStorage.getItem("jwt")) {
await renew(localStorage.getItem("jwt"));
if (jwt) {
await renew(jwt);
}
} catch (_) {
console.warn('Invalid JWT token in storage') // eslint-disable-line

6
go.mod
View File

@ -4,6 +4,7 @@ go 1.18
require (
github.com/asdine/storm/v3 v3.2.1
github.com/coreos/go-oidc/v3 v3.5.0
github.com/disintegration/imaging v1.6.2
github.com/dsoprea/go-exif/v3 v3.0.0-20201216222538-db167117f483
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
@ -25,6 +26,7 @@ require (
go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.6.0
golang.org/x/image v0.5.0
golang.org/x/oauth2 v0.5.0
golang.org/x/text v0.7.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v2 v2.4.0
@ -38,8 +40,10 @@ require (
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-errors/errors v1.1.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
@ -59,6 +63,8 @@ require (
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/sys v0.5.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

23
go.sum
View File

@ -23,6 +23,7 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@ -54,6 +55,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-oidc/v3 v3.5.0 h1:VxKtbccHZxs8juq7RdJntSqtXFtde9YpNpGn0yqgEHw=
github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -91,6 +94,8 @@ github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
@ -123,7 +128,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -140,6 +147,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@ -237,6 +245,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -274,6 +283,7 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
@ -352,6 +362,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -363,6 +375,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -414,11 +430,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -426,6 +444,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -572,7 +591,11 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=

View File

@ -112,6 +112,8 @@ var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, e
return http.StatusForbidden, nil
} else if err != nil {
return http.StatusInternalServerError, err
} else if user != nil && user.AuthSource == "oidc" {
return setTokenCookie(w, r, d, user)
} else {
return printToken(w, r, d, user)
}
@ -176,7 +178,7 @@ var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data
return printToken(w, r, d, d.user)
})
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) {
func getToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (string, error) {
claims := &authToken{
User: userInfo{
ID: user.ID,
@ -198,6 +200,12 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signed, err := token.SignedString(d.settings.Key)
return signed, err
}
func printToken(w http.ResponseWriter, r *http.Request, d *data, user *users.User) (int, error) {
signed, err := getToken(w, r, d, user)
if err != nil {
return http.StatusInternalServerError, err
}
@ -208,3 +216,15 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use
}
return 0, nil
}
func setTokenCookie(w http.ResponseWriter, r *http.Request, d *data, user *users.User) (int, error) {
signed, err := getToken(w, r, d, user)
if err != nil {
return http.StatusInternalServerError, err
}
w.Header().Set("Set-Cookie", "auth="+signed+"; path=/")
http.Redirect(w, r, "/files", http.StatusMovedPermanently)
return 0, nil
}

View File

@ -18,7 +18,7 @@ import (
"github.com/filebrowser/filebrowser/v2/version"
)
func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys fs.FS, file, contentType string) (int, error) {
func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *data, fSys fs.FS, file, contentType string) (int, error) {
w.Header().Set("Content-Type", contentType)
auther, err := d.store.Auth.Get(d.settings.AuthMethod)
@ -74,6 +74,21 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
}
}
if d.settings.AuthMethod == auth.MethodOIDCAuth {
raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:govet
if err != nil {
return http.StatusInternalServerError, err
}
auther := raw.(*auth.OIDCAuth)
cookie, _ := r.Cookie("auth")
if cookie == nil {
auther.OIDC.InitAuthFlow(w, r)
return 0, nil
}
}
b, err := json.Marshal(data)
if err != nil {
return http.StatusInternalServerError, err

View File

@ -18,6 +18,8 @@ func (s authBackend) Get(t settings.AuthMethod) (auth.Auther, error) {
switch t {
case auth.MethodJSONAuth:
auther = &auth.JSONAuth{}
case auth.MethodOIDCAuth:
auther = &auth.OIDCAuth{}
case auth.MethodProxyAuth:
auther = &auth.ProxyAuth{}
case auth.MethodHookAuth:

View File

@ -44,6 +44,12 @@ type oldConf struct {
Secret string `json:"secret" yaml:"secret" toml:"secret"`
Host string `json:"host" yaml:"host" toml:"host"`
} `json:"recaptcha" yaml:"recaptcha" toml:"recaptcha"`
OIDC struct {
ClientID string `json:"clientID" yaml:"clientID" toml:"clientID"`
ClientSecret string `json:"clientSecret" yaml:"clientSecret" toml:"clientSecret"`
Issuer string `json:"issuer" yaml:"issuer" toml:"issuer"`
RedirectURL string `json:"redirectURL" yaml:"redirectURL" toml:"redirectURL"`
} `json:"oidc" yaml:"oidc" toml:"oidc"`
Auth oldAuth `json:"auth" yaml:"auth" toml:"auth"`
}
@ -150,6 +156,16 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
case "proxy":
auther = &auth.ProxyAuth{Header: cfg.Auth.Header}
s.AuthMethod = auth.MethodProxyAuth
case "oidc":
auther = &auth.OIDCAuth{
OIDC: &auth.OAuthClient{
ClientID: cfg.OIDC.ClientID,
ClientSecret: cfg.OIDC.ClientSecret,
Issuer: cfg.OIDC.Issuer,
RedirectURL: cfg.OIDC.RedirectURL,
},
}
s.AuthMethod = auth.MethodOIDCAuth
case "hook":
auther = &auth.HookAuth{Command: cfg.Auth.Command}
s.AuthMethod = auth.MethodHookAuth

View File

@ -36,6 +36,7 @@ type User struct {
Rules []rules.Rule `json:"rules"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
AuthSource string `json:"authSource"`
}
// GetRules implements rules.Provider.