aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/ui
diff options
context:
space:
mode:
authorGravatar Frédéric Guillot <fred@miniflux.net>2017-11-22 22:22:33 -0800
committerGravatar Frédéric Guillot <fred@miniflux.net>2017-11-22 22:22:33 -0800
commitcc6d272eb7719bcca02c7f36a1badbeecb153759 (patch)
tree2fd6e92dcaa19faccfc25ff67499abcc6dabbaaf /server/ui
parent9877051f12621aa71daad520caa2847c47c746f8 (diff)
Add OAuth2 authentication
Diffstat (limited to 'server/ui')
-rw-r--r--server/ui/controller/controller.go5
-rw-r--r--server/ui/controller/login.go9
-rw-r--r--server/ui/controller/oauth2.go123
3 files changed, 134 insertions, 3 deletions
diff --git a/server/ui/controller/controller.go b/server/ui/controller/controller.go
index 4aa6085..a8f5218 100644
--- a/server/ui/controller/controller.go
+++ b/server/ui/controller/controller.go
@@ -5,6 +5,7 @@
package controller
import (
+ "github.com/miniflux/miniflux2/config"
"github.com/miniflux/miniflux2/model"
"github.com/miniflux/miniflux2/reader/feed"
"github.com/miniflux/miniflux2/reader/opml"
@@ -25,6 +26,7 @@ func (t tplParams) Merge(d tplParams) tplParams {
// Controller contains all HTTP handlers for the user interface.
type Controller struct {
+ cfg *config.Config
store *storage.Storage
pool *scheduler.WorkerPool
feedHandler *feed.Handler
@@ -51,8 +53,9 @@ func (c *Controller) getCommonTemplateArgs(ctx *core.Context) (tplParams, error)
}
// NewController returns a new Controller.
-func NewController(store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler, opmlHandler *opml.Handler) *Controller {
+func NewController(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler, opmlHandler *opml.Handler) *Controller {
return &Controller{
+ cfg: cfg,
store: store,
pool: pool,
feedHandler: feedHandler,
diff --git a/server/ui/controller/login.go b/server/ui/controller/login.go
index aaaf0b6..2571f6e 100644
--- a/server/ui/controller/login.go
+++ b/server/ui/controller/login.go
@@ -5,15 +5,17 @@
package controller
import (
- "github.com/miniflux/miniflux2/server/core"
- "github.com/miniflux/miniflux2/server/ui/form"
"log"
"net/http"
"time"
+ "github.com/miniflux/miniflux2/server/core"
+ "github.com/miniflux/miniflux2/server/ui/form"
+
"github.com/tomasen/realip"
)
+// ShowLoginPage shows the login form.
func (c *Controller) ShowLoginPage(ctx *core.Context, request *core.Request, response *core.Response) {
if ctx.IsAuthenticated() {
response.Redirect(ctx.Route("unread"))
@@ -25,6 +27,7 @@ func (c *Controller) ShowLoginPage(ctx *core.Context, request *core.Request, res
})
}
+// CheckLogin validates the username/password and redirects the user to the unread page.
func (c *Controller) CheckLogin(ctx *core.Context, request *core.Request, response *core.Response) {
authForm := form.NewAuthForm(request.Request())
tplParams := tplParams{
@@ -49,6 +52,7 @@ func (c *Controller) CheckLogin(ctx *core.Context, request *core.Request, respon
request.Request().UserAgent(),
realip.RealIP(request.Request()),
)
+
if err != nil {
response.HTML().ServerError(err)
return
@@ -68,6 +72,7 @@ func (c *Controller) CheckLogin(ctx *core.Context, request *core.Request, respon
response.Redirect(ctx.Route("unread"))
}
+// Logout destroy the session and redirects the user to the login page.
func (c *Controller) Logout(ctx *core.Context, request *core.Request, response *core.Response) {
user := ctx.LoggedUser()
diff --git a/server/ui/controller/oauth2.go b/server/ui/controller/oauth2.go
new file mode 100644
index 0000000..c43d707
--- /dev/null
+++ b/server/ui/controller/oauth2.go
@@ -0,0 +1,123 @@
+// Copyright 2017 Frédéric Guillot. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package controller
+
+import (
+ "log"
+ "net/http"
+
+ "github.com/miniflux/miniflux2/config"
+ "github.com/miniflux/miniflux2/model"
+ "github.com/miniflux/miniflux2/server/core"
+ "github.com/miniflux/miniflux2/server/oauth2"
+ "github.com/tomasen/realip"
+)
+
+// OAuth2Redirect redirects the user to the consent page to ask for permission.
+func (c *Controller) OAuth2Redirect(ctx *core.Context, request *core.Request, response *core.Response) {
+ provider := request.StringParam("provider", "")
+ if provider == "" {
+ log.Println("[OAuth2] Invalid or missing provider")
+ response.Redirect(ctx.Route("login"))
+ return
+ }
+
+ authProvider, err := getOAuth2Manager(c.cfg).Provider(provider)
+ if err != nil {
+ log.Println("[OAuth2]", err)
+ response.Redirect(ctx.Route("login"))
+ return
+ }
+
+ response.Redirect(authProvider.GetRedirectURL(ctx.CsrfToken()))
+}
+
+// OAuth2Callback receives the authorization code and create a new session.
+func (c *Controller) OAuth2Callback(ctx *core.Context, request *core.Request, response *core.Response) {
+ provider := request.StringParam("provider", "")
+ if provider == "" {
+ log.Println("[OAuth2] Invalid or missing provider")
+ response.Redirect(ctx.Route("login"))
+ return
+ }
+
+ code := request.QueryStringParam("code", "")
+ if code == "" {
+ log.Println("[OAuth2] No code received on callback")
+ response.Redirect(ctx.Route("login"))
+ return
+ }
+
+ state := request.QueryStringParam("state", "")
+ if state != ctx.CsrfToken() {
+ log.Println("[OAuth2] Invalid state value")
+ response.Redirect(ctx.Route("login"))
+ return
+ }
+
+ authProvider, err := getOAuth2Manager(c.cfg).Provider(provider)
+ if err != nil {
+ log.Println("[OAuth2]", err)
+ response.Redirect(ctx.Route("login"))
+ return
+ }
+
+ profile, err := authProvider.GetProfile(code)
+ if err != nil {
+ log.Println("[OAuth2]", err)
+ response.Redirect(ctx.Route("login"))
+ return
+ }
+
+ user, err := c.store.GetUserByExtraField(profile.Key, profile.ID)
+ if err != nil {
+ response.HTML().ServerError(err)
+ return
+ }
+
+ if user == nil {
+ user = model.NewUser()
+ user.Username = profile.Username
+ user.IsAdmin = false
+ user.Extra[profile.Key] = profile.ID
+
+ if err := c.store.CreateUser(user); err != nil {
+ response.HTML().ServerError(err)
+ return
+ }
+ }
+
+ sessionToken, err := c.store.CreateSession(
+ user.Username,
+ request.Request().UserAgent(),
+ realip.RealIP(request.Request()),
+ )
+
+ if err != nil {
+ response.HTML().ServerError(err)
+ return
+ }
+
+ log.Printf("[UI:OAuth2Callback] username=%s just logged in\n", user.Username)
+
+ cookie := &http.Cookie{
+ Name: "sessionID",
+ Value: sessionToken,
+ Path: "/",
+ Secure: request.IsHTTPS(),
+ HttpOnly: true,
+ }
+
+ response.SetCookie(cookie)
+ response.Redirect(ctx.Route("unread"))
+}
+
+func getOAuth2Manager(cfg *config.Config) *oauth2.Manager {
+ return oauth2.NewManager(
+ cfg.Get("OAUTH2_CLIENT_ID", ""),
+ cfg.Get("OAUTH2_CLIENT_SECRET", ""),
+ cfg.Get("OAUTH2_REDIRECT_URL", ""),
+ )
+}