Skip to content

Commit

Permalink
refactoring: session, models/tokens, oidc, ctrl
Browse files Browse the repository at this point in the history
  • Loading branch information
wsalles committed Dec 6, 2021
1 parent eb1488d commit b314e4c
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 67 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Authentication Service

This is a rewrite of the [arrikto/oidc-authservice](https://github.com/arrikto/oidc-authservice) project to be as simple as possible.
This is a simple project that makes the authentication service with the OIDC flow.

An Authentication Service is an HTTP Server that an API Gateway (eg Ambassador, Envoy) asks if an incoming request is authorized.
Basically, you need to have an OIDC Provider ([config parameters](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest)) and configure the Authentication Service.

* * *
## OpenID Connect

[OpenID Connect (OIDC)](http://openid.net/connect/) is an authentication layer on top of the OAuth 2.0 protocol. As OAuth 2.0 is fully supported by OpenID Connect, existing OAuth 2.0 implementations work with it out of the box.

Currently it only supports OIDC's [Authorization Code Flow](http://openid.net/specs/openid-connect-basic-1_0.html#CodeFlow), similar to OAuth 2.0 Authorization Code Grant.
Currently, it only supports OIDC's [Authorization Code Flow](http://openid.net/specs/openid-connect-basic-1_0.html#CodeFlow), similar to OAuth 2.0 Authorization Code Grant.

* * *
## About 👯‍♂️
Expand Down
1 change: 0 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func main() {
httpServer := server.NewServer(logger, serverConfiguration)
restController.Boot(httpServer)
httpServer.Run(context.Background())
logger.Infof("Running Server [%v:%v]", serverConfiguration.Host, serverConfiguration.Port)

for err := range errChan {
logger.Error(err)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ module github.com/ydataai/authentication-service
go 1.17

require (
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.1.0
github.com/gin-gonic/gin v1.7.4
github.com/golang-jwt/jwt/v4 v4.2.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/ydataai/go-core v0.2.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
Expand All @@ -24,7 +25,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
Expand Down
9 changes: 6 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
Expand Down Expand Up @@ -171,6 +172,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -396,7 +399,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
Expand Down Expand Up @@ -582,6 +584,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
Expand Down
23 changes: 18 additions & 5 deletions internal/clients/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"time"

"github.com/coreos/go-oidc"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/ydataai/go-core/pkg/common/logging"
"golang.org/x/oauth2"
)
Expand All @@ -15,15 +15,15 @@ type OIDCClient struct {
OAuth2Config *oauth2.Config
Verifier *oidc.IDTokenVerifier
provider *oidc.Provider
ReadyzFunc func() bool
readyzFunc func() bool
logger logging.Logger
}

// NewOIDCClient defines a new values for the server
func NewOIDCClient(logger logging.Logger, config OIDCConfiguration) *OIDCClient {
return &OIDCClient{
configuration: config,
ReadyzFunc: func() bool { return false },
readyzFunc: func() bool { return false },
logger: logger,
}
}
Expand All @@ -43,13 +43,15 @@ func (oc *OIDCClient) Setup() {
RedirectURL: oc.configuration.OIDCRedirectURL,
Scopes: oc.configuration.OIDCScopes,
}

oidcConfig := &oidc.Config{
ClientID: oc.configuration.ClientID,
}

oc.Verifier = oc.provider.Verifier(oidcConfig)
}

// isAvailable ensures that the service is available after identifying an OIDC Provider.
func (oc *OIDCClient) isAvailable(ctx context.Context) {
var err error

Expand All @@ -58,9 +60,20 @@ func (oc *OIDCClient) isAvailable(ctx context.Context) {
if err == nil {
break
}
oc.logger.Errorf("OIDC provider setup failed, retrying in 10 seconds: %v", err)
oc.logger.Errorf("[✖️] OIDC Provider setup failed. Error: %v | Retrying in 10 seconds...", err)
time.Sleep(10 * time.Second)
}
oc.logger.Info("[✔️] Connected to OIDC Provider")

oc.readyzFunc = func() bool { return true }
}

// GetReadyzFunc make sure if OIDC Provider is ready.
func (oc *OIDCClient) GetReadyzFunc() bool {
return oc.readyzFunc()
}

oc.ReadyzFunc = func() bool { return true }
// GetProvider gets provider function
func (oc *OIDCClient) GetProvider() *oidc.Provider {
return oc.provider
}
52 changes: 44 additions & 8 deletions internal/controllers/rest_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -46,40 +47,75 @@ func (rc RESTController) Boot(s *server.Server) {
// RedirectToOIDCProvider is the handler responsible for redirecting to the OIDC Provider
func (rc RESTController) RedirectToOIDCProvider(w http.ResponseWriter, r *http.Request) {
if !rc.oidcService.GetReadyzFunc() {
rc.logger.Error("OIDC provider is not ready yet or setup failed")
rc.logger.Error("OIDC Provider is not ready yet or setup failed")
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
return
}

rc.sessionService.CreateCookie(w, r)
// Sets a temporary state and nonce to validate when there is callback from the OIDC Provider.
rc.sessionService.SetState()
rc.sessionService.SetNonce()

rc.logger.Info("Redirecting to OIDC Provider...")
http.Redirect(w, r,
rc.oidcService.CreateOIDCProviderURL(rc.sessionService.State, rc.sessionService.Nonce),
rc.oidcService.CreateOIDCProviderURL(rc.sessionService.GetState(), rc.sessionService.GetNonce()),
http.StatusFound,
)
}

// OIDCProviderCallback returns with authentication code
func (rc RESTController) OIDCProviderCallback(w http.ResponseWriter, r *http.Request) {
if !rc.oidcService.GetReadyzFunc() {
rc.logger.Error("OIDC provider is not ready yet or setup failed")
rc.logger.Error("OIDC Provider is not ready yet or setup failed")
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
return
}

ctx, cancel := context.WithTimeout(context.Background(), rc.configuration.HTTPRequestTimeout)
defer cancel()

token, err := rc.oidcService.TokenClaims(ctx, w, r)
token, err := rc.oidcService.ClaimsToken(ctx, w, r)
if err != nil {
return
}

// creates JSON data to display as body content
jsonBody, err := json.Marshal(token)
// To improve security, we need to combine the state and nonce created earlier
// with the callback from the OIDC Provider.
if !rc.sessionService.MatchState(r) {
msg := "state did not match"
rc.logger.Error(msg)
http.Error(w, msg, http.StatusBadRequest)
return
}

nonceToken, err := rc.oidcService.GetValueFromToken("nonce", token)
if err != nil {
msg := fmt.Sprintf("An unexpected error occurred when getting the nonce from ID Token. Err: %v", err)
rc.logger.Errorf(msg)
http.Error(w, msg, http.StatusBadRequest)
return
}
if !rc.sessionService.MatchNonce(nonceToken.(string), r) {
msg := "nonce did not match"
rc.logger.Error(msg)
http.Error(w, msg, http.StatusBadRequest)
return
}

jwt, err := rc.sessionService.CreateJWT(&token.CustomClaims)
if err != nil {
msg := fmt.Sprintf("An error occurred while creating a JWT. Error: %v", err)
rc.logger.Error(msg)
http.Error(w, msg, http.StatusInternalServerError)
return
}

// If all goes well, then create the JSON data to display as body content.
jsonBody, err := json.Marshal(jwt)
if err != nil {
rc.logger.Errorf("An error occurred while validating some tokens. Error: %v", err)
msg := fmt.Sprintf("An error occurred while validating some tokens. Error: %v", err)
rc.logger.Errorf(msg)
http.Error(w, msg, http.StatusInternalServerError)
return
}

Expand Down
36 changes: 27 additions & 9 deletions internal/models/tokens.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
package models

import "golang.org/x/oauth2"
import (
"encoding/json"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/oauth2"
)

// Tokens defines the token struct
type Tokens struct {
OAuth2Token *oauth2.Token
CustomClaims CustomClaims
OAuth2Token *oauth2.Token
IDTokenClaims *json.RawMessage
UserInfo *oidc.UserInfo
CustomClaims CustomClaims
}

// CustomClaims defines the custom claims struct
type CustomClaims struct {
Name string `json:"name"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified" default:"false"`
Picture string `json:"picture"`
Expiry int64 `json:"exp"`
IssuedAt int64 `json:"iat"`
AccessToken string `json:"access_token,omitempty"`
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty" default:"false"`
Picture string `json:"picture,omitempty"`
Profile string `json:"profile,omitempty"`
// The "aud" (audience) claim identifies the recipients that the JWT is intended for.
// See more at: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
Audience string `json:"aud,omitempty"`
// The "sub" (subject) claim identifies the principal that is the subject of the JWT.
// See more at: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2
Subject string `json:"sub,omitempty"`
// RegisteredClaims are a structured version of the JWT Claims Set,
// restricted to Registered Claim Names, as referenced at
// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
jwt.RegisteredClaims
}
Loading

0 comments on commit b314e4c

Please sign in to comment.