Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
115 changes: 107 additions & 8 deletions core/auth/Readme.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,124 @@
# Auth

## Design
In general, all identifying brokers are able to be specified more than once, and at any point, there can be zero to many identities available.
The Auth module provides access to authentication logic.
It comes with the following features:

* Basic models for "Identity" and a concept how to register new "RequestIdentifiers"
* A "WebModule" that can be used to handle and start login flows (or authentication flows)
* Implementation of:
* OpenIDConnect Authentication Flow - including the possibility to fake authentications for testing and development purposes
* Identify requests with OAuth Bearer Token
* And a simple HTTP Authentication (BasicAuth) implementation


## Usage Examples

### Use OIDC in your (web) application
A typical usecase is, that you want to authenticate users against a given single sign on system via OICD (Open ID Connect).

Therefore add the `oauth.Module` to the bootstrap of your application. (The module will automatically add the `auth.WebModule` as a dependency:

```go

package main

import (
"flamingo.me/dingo"
"flamingo.me/flamingo/v3"
"flamingo.me/flamingo/v3/core/auth/oauth"
)

func main() {
flamingo.App([]dingo.Module{
new(oauth.Module),
})
}
```

Then configure the Broker that you want to use in your application configuration. For example if you want to [use google as an open id connect provider](https://developers.google.com/identity/openid-connect/openid-connect) you can configure it like this:

```yaml
flamingo.debug.mode: true

core.auth.web.broker:
-
broker: "google"
clientID: **************
clientSecret: **********
enableEndSessionEndpoint: true
# request offline_access scope to receive refresh
enableOfflineToken: false
endpoint: "https://accounts.google.com"
typ: "oidc"


flamingo.session.cookie.secure: false

```
Then you can trigger the authentication process by calling the route: `http://localhost:3322/core/auth/login/google`
You can find this example in the folder "example/google".

To access Identity informations you can use the `auth.WebIdentityService` inside your code:

```go
// Inject dependencies
func (controller *testController) Inject(webIdentityService *auth.WebIdentityService) *testController {
controller.webIdentityService = webIdentityService
return controller
}

func (controller *testController) Index(ctx context.Context, req *web.Request) web.Result {
identity := controller.webIdentityService.Identify(ctx, req)

if identity == nil {
// not logged in
} else {
// logged in
oidcIdentity, _ := identity.(oauth.OpenIDIdentity)
_ := oidcIdentity.IDToken()
}
...
}

```



### Use multiple Brokers in one application:

There is also an advanced example in the "example" folder, which comes with a docker compose setup booting up two [dex](https://dexidp.io/) and two [keycloak](https://www.aoe.com/techradar/tools/keycloak.html) and a confiured flamingo application that uses all kind of Authentication Providers - including basic auth and fake implementations for testing.

In debug mode (`core.auth.web.debugController`, default to `flamingo.debug.mode`) there is http://localhost:3322/core/auth/debug for debugging.


## Concept and Design

The module uses the following concepts:

* An *Identity* is the object that represents "someone" or "something". It just has a "subject" (e.g. a username or id). An *Identity* is "identified" by a *Broker*
* In the module we use the following differentiation:
* Identify: Is the process of checking the request for an existing Identity information. (Some broker may use a state to detect an Identity)
* Authenticate: Is the process of requesting an Identity. (e.g. by providing a login form or redirection to an external Authprovider)
* A *Broker* is a specific implementation of an Authorisation and Identification logic/sheme. A Broker may just implement the basic *RequestIdentifier* interface or also *WebAuthenticater*, *WebCallbacker*, *WebLogouter* and *WebIdenityRefresher* interfaces if the implemented Authorisationlogic needs it.
* Identity Brokers have a type and can be registered and configured under a "Brokername". In general, all identifying brokers types can be configured more than once, and at any point, there can be zero to many identities available.

More details in the following chapters:

### Identity
The `auth.Identity` is the minimum available information about an identified request/context situation.
It consists of `Broker` and `Subject`, where the broker identifies the authenticating party and the subject identifies the primary subject the identity identifies.
It consists of `Broker` and `Subject`, where the broker identifies the authenticating broker (authenticating party) and the subject identifies the primary subject the identity identifies.

### WebIdentifier
### RequestIdentifier
The WebIdentifier primarily identifies incoming `web.Request`s.
This could be done by means of inspecting the session, request data (auth header), etc.

### WebAuthenticator
WebIdentifier who implements the authenticator interface is able to trigger authentication.
This can be a redirect to an external page, setting HTTP headers, or presenting a login form.
WebIdentifier who implements the authenticator interface is able to trigger authentication. This can be a redirect to an external page, setting HTTP headers, or presenting a login form.

### WebLogouter
Once a logout has triggered all identifiers who implement either one of the logout methods are called.
The WebLogouter will destroy session data etc., while the WebLogouterWithRedirect can return a redirect (e.g. to an OpenID Connect server).

Multiple redirects are handled automagically.

## Debug
In debug mode (`core.auth.web.debugController`, default to `flamingo.debug.mode`) there is http://localhost:3322/core/auth/debug for debugging.

20 changes: 20 additions & 0 deletions core/auth/example/google/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# About the example

This example uses Google as OIDC Provider.
To test it you need to create an application in the developer area of google.
See here for a description: https://developers.google.com/identity/openid-connect/openid-connect


# Run the example

Start the flamingo app and pass the CLIENTID and CLIENTSECRET as ENV variables:

`CLIENTID="4**********.apps.googleusercontent.com" && CLIENTSECRET="************_i4Z57XbhN20P4f" && go run main.go serve`

Then open http://localhost:3322

In the Debug view you can see the reqistered brokers and you can start the authentication:

http://localhost:3322/core/auth/debug

![debugaction.png](debugaction.png)
23 changes: 23 additions & 0 deletions core/auth/example/google/config/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
flamingo.debug.mode: true

core.auth.web.broker:
-
broker: "google"
claims:
idToken:
email: email
email_verified: email_verified
given_name: given_name
userInfo:
email: email
email_verified: email_verified
given_name: given_name
clientID: 447471420712-fdfmg20tlp57cdbv3qmmp6p2ikjv3so7.apps.googleusercontent.com
clientSecret: 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
Comment on lines +15 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really want to commit that? I would revoke that...

enableEndSessionEndpoint: true
enableOfflineToken: false
endpoint: "https://accounts.google.com"
typ: "oidc"


flamingo.session.cookie.secure: false
Binary file added core/auth/example/google/debugaction.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions core/auth/example/google/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"context"
"net/http"
"strings"

"flamingo.me/dingo"
"flamingo.me/flamingo/v3"
"flamingo.me/flamingo/v3/core/auth"
"flamingo.me/flamingo/v3/core/auth/oauth"
"flamingo.me/flamingo/v3/framework/web"
)

func main() {
flamingo.App([]dingo.Module{
new(oauth.Module),
new(testModule),
})
}

type testModule struct{}

func (*testModule) Configure(injector *dingo.Injector) {
web.BindRoutes(injector, new(routes))
}

type routes struct {
testController *testController
}

func (r *routes) Inject(controller *testController) *routes {
r.testController = controller
return r
}

func (r *routes) Routes(registry *web.RouterRegistry) {
registry.MustRoute("/", "index")
registry.HandleAny("index", r.testController.Index)
}

type testController struct {
responder *web.Responder
webIdentityService *auth.WebIdentityService
}

// Inject dependencies
func (controller *testController) Inject(responder *web.Responder, webIdentityService *auth.WebIdentityService) *testController {
controller.responder = responder
controller.webIdentityService = webIdentityService

return controller
}

func (controller *testController) Index(ctx context.Context, req *web.Request) web.Result {
identity := controller.webIdentityService.Identify(ctx, req)
body := ""

if identity == nil {
body = "Hello Guest"
} else {
oidcIdentity, _ := identity.(oauth.OpenIDIdentity)
body = "Hello " + oidcIdentity.Subject() + " IDToken Expired: " + oidcIdentity.IDToken().Expiry.GoString()
}

return controller.responder.HTTP(
http.StatusOK,
strings.NewReader(body),
)
}
File renamed without changes.
File renamed without changes.
61 changes: 61 additions & 0 deletions core/auth/example/multiple/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module multiple
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need a new module here


go 1.19

require (
flamingo.me/dingo v0.2.10
flamingo.me/flamingo/v3 v3.4.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would require ourself but in a different version, I don't think that's necessary

)

require (
contrib.go.opencensus.io/exporter/jaeger v0.2.1 // indirect
contrib.go.opencensus.io/exporter/prometheus v0.4.1 // indirect
contrib.go.opencensus.io/exporter/zipkin v0.1.2 // indirect
cuelang.org/go v0.0.15 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cockroachdb/apd/v2 v2.0.1 // indirect
github.com/coreos/go-oidc/v3 v3.4.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-kit/log v0.1.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect
github.com/openzipkin/zipkin-go v0.4.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.11.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.28.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/prometheus/statsd_exporter v0.21.0 // indirect
github.com/rbcervilla/redisstore/v8 v8.1.0 // indirect
github.com/spf13/cobra v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect
github.com/zemirco/memorystore v0.0.0-20160308183530-ecd57e5134f6 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/automaxprocs v1.5.1 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 // indirect
golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect
golang.org/x/text v0.3.8 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/api v0.84.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading