Oauth2 consumer (#679)
* initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
This commit is contained in:
parent
fd941db246
commit
01d957677f
76 changed files with 7275 additions and 137 deletions
199
vendor/github.com/gorilla/sessions/doc.go
generated
vendored
Normal file
199
vendor/github.com/gorilla/sessions/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,199 @@
|
|||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package sessions provides cookie and filesystem sessions and
|
||||
infrastructure for custom session backends.
|
||||
|
||||
The key features are:
|
||||
|
||||
* Simple API: use it as an easy way to set signed (and optionally
|
||||
encrypted) cookies.
|
||||
* Built-in backends to store sessions in cookies or the filesystem.
|
||||
* Flash messages: session values that last until read.
|
||||
* Convenient way to switch session persistency (aka "remember me") and set
|
||||
other attributes.
|
||||
* Mechanism to rotate authentication and encryption keys.
|
||||
* Multiple sessions per request, even using different backends.
|
||||
* Interfaces and infrastructure for custom session backends: sessions from
|
||||
different stores can be retrieved and batch-saved using a common API.
|
||||
|
||||
Let's start with an example that shows the sessions API in a nutshell:
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
var store = sessions.NewCookieStore([]byte("something-very-secret"))
|
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Get a session. We're ignoring the error resulted from decoding an
|
||||
// existing session: Get() always returns a session, even if empty.
|
||||
session, err := store.Get(r, "session-name")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Set some session values.
|
||||
session.Values["foo"] = "bar"
|
||||
session.Values[42] = 43
|
||||
// Save it before we write to the response/return from the handler.
|
||||
session.Save(r, w)
|
||||
}
|
||||
|
||||
First we initialize a session store calling NewCookieStore() and passing a
|
||||
secret key used to authenticate the session. Inside the handler, we call
|
||||
store.Get() to retrieve an existing session or a new one. Then we set some
|
||||
session values in session.Values, which is a map[interface{}]interface{}.
|
||||
And finally we call session.Save() to save the session in the response.
|
||||
|
||||
Note that in production code, we should check for errors when calling
|
||||
session.Save(r, w), and either display an error message or otherwise handle it.
|
||||
|
||||
Save must be called before writing to the response, otherwise the session
|
||||
cookie will not be sent to the client.
|
||||
|
||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
|
||||
with context.ClearHandler as or else you will leak memory! An easy way to do this
|
||||
is to wrap the top-level mux when calling http.ListenAndServe:
|
||||
|
||||
http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
|
||||
|
||||
The ClearHandler function is provided by the gorilla/context package.
|
||||
|
||||
That's all you need to know for the basic usage. Let's take a look at other
|
||||
options, starting with flash messages.
|
||||
|
||||
Flash messages are session values that last until read. The term appeared with
|
||||
Ruby On Rails a few years back. When we request a flash message, it is removed
|
||||
from the session. To add a flash, call session.AddFlash(), and to get all
|
||||
flashes, call session.Flashes(). Here is an example:
|
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Get a session.
|
||||
session, err := store.Get(r, "session-name")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the previously flashes, if any.
|
||||
if flashes := session.Flashes(); len(flashes) > 0 {
|
||||
// Use the flash values.
|
||||
} else {
|
||||
// Set a new flash.
|
||||
session.AddFlash("Hello, flash messages world!")
|
||||
}
|
||||
session.Save(r, w)
|
||||
}
|
||||
|
||||
Flash messages are useful to set information to be read after a redirection,
|
||||
like after form submissions.
|
||||
|
||||
There may also be cases where you want to store a complex datatype within a
|
||||
session, such as a struct. Sessions are serialised using the encoding/gob package,
|
||||
so it is easy to register new datatypes for storage in sessions:
|
||||
|
||||
import(
|
||||
"encoding/gob"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
type Person struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
Age int
|
||||
}
|
||||
|
||||
type M map[string]interface{}
|
||||
|
||||
func init() {
|
||||
|
||||
gob.Register(&Person{})
|
||||
gob.Register(&M{})
|
||||
}
|
||||
|
||||
As it's not possible to pass a raw type as a parameter to a function, gob.Register()
|
||||
relies on us passing it a value of the desired type. In the example above we've passed
|
||||
it a pointer to a struct and a pointer to a custom type representing a
|
||||
map[string]interface. (We could have passed non-pointer values if we wished.) This will
|
||||
then allow us to serialise/deserialise values of those types to and from our sessions.
|
||||
|
||||
Note that because session values are stored in a map[string]interface{}, there's
|
||||
a need to type-assert data when retrieving it. We'll use the Person struct we registered above:
|
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := store.Get(r, "session-name")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve our struct and type-assert it
|
||||
val := session.Values["person"]
|
||||
var person = &Person{}
|
||||
if person, ok := val.(*Person); !ok {
|
||||
// Handle the case that it's not an expected type
|
||||
}
|
||||
|
||||
// Now we can use our person object
|
||||
}
|
||||
|
||||
By default, session cookies last for a month. This is probably too long for
|
||||
some cases, but it is easy to change this and other attributes during
|
||||
runtime. Sessions can be configured individually or the store can be
|
||||
configured and then all sessions saved using it will use that configuration.
|
||||
We access session.Options or store.Options to set a new configuration. The
|
||||
fields are basically a subset of http.Cookie fields. Let's change the
|
||||
maximum age of a session to one week:
|
||||
|
||||
session.Options = &sessions.Options{
|
||||
Path: "/",
|
||||
MaxAge: 86400 * 7,
|
||||
HttpOnly: true,
|
||||
}
|
||||
|
||||
Sometimes we may want to change authentication and/or encryption keys without
|
||||
breaking existing sessions. The CookieStore supports key rotation, and to use
|
||||
it you just need to set multiple authentication and encryption keys, in pairs,
|
||||
to be tested in order:
|
||||
|
||||
var store = sessions.NewCookieStore(
|
||||
[]byte("new-authentication-key"),
|
||||
[]byte("new-encryption-key"),
|
||||
[]byte("old-authentication-key"),
|
||||
[]byte("old-encryption-key"),
|
||||
)
|
||||
|
||||
New sessions will be saved using the first pair. Old sessions can still be
|
||||
read because the first pair will fail, and the second will be tested. This
|
||||
makes it easy to "rotate" secret keys and still be able to validate existing
|
||||
sessions. Note: for all pairs the encryption key is optional; set it to nil
|
||||
or omit it and and encryption won't be used.
|
||||
|
||||
Multiple sessions can be used in the same request, even with different
|
||||
session backends. When this happens, calling Save() on each session
|
||||
individually would be cumbersome, so we have a way to save all sessions
|
||||
at once: it's sessions.Save(). Here's an example:
|
||||
|
||||
var store = sessions.NewCookieStore([]byte("something-very-secret"))
|
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Get a session and set a value.
|
||||
session1, _ := store.Get(r, "session-one")
|
||||
session1.Values["foo"] = "bar"
|
||||
// Get another session and set another value.
|
||||
session2, _ := store.Get(r, "session-two")
|
||||
session2.Values[42] = 43
|
||||
// Save all sessions.
|
||||
sessions.Save(r, w)
|
||||
}
|
||||
|
||||
This is possible because when we call Get() from a session store, it adds the
|
||||
session to a common registry. Save() uses it to save all registered sessions.
|
||||
*/
|
||||
package sessions
|
Loading…
Add table
Add a link
Reference in a new issue