prejwt
This commit is contained in:
parent
9224b21db7
commit
6aa2c46717
8 changed files with 107 additions and 66 deletions
|
@ -25,7 +25,7 @@ var (
|
||||||
type Authenticator struct {
|
type Authenticator struct {
|
||||||
store AuthStore
|
store AuthStore
|
||||||
flows *AuthFlowManager
|
flows *AuthFlowManager
|
||||||
authCodes authCodeStore
|
authCodes authCodeStore
|
||||||
providers map[string]provider.AuthProvider
|
providers map[string]provider.AuthProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,3 +112,5 @@ func (a *Authenticator) Check(f *LoginFlow, req *http.Request, rm map[string]int
|
||||||
|
|
||||||
return nil, clientID, ErrInvalidAuth
|
return nil, clientID, ErrInvalidAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//func (a *Authenticator) GetOrCreateCreds(
|
||||||
|
|
|
@ -103,7 +103,6 @@ func (f *LoginFlow) progress(a *Authenticator, c echo.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusBadRequest, f.ShowForm([]string{err.Error()}))
|
return c.JSON(http.StatusBadRequest, f.ShowForm([]string{err.Error()}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
user, clientID, err := a.Check(f, c.Request(), rm)
|
user, clientID, err := a.Check(f, c.Request(), rm)
|
||||||
switch err {
|
switch err {
|
||||||
|
|
|
@ -26,12 +26,23 @@ type HAUser struct {
|
||||||
|
|
||||||
func (hau *HAUser) UserData() provider.ProviderUser {
|
func (hau *HAUser) UserData() provider.ProviderUser {
|
||||||
return &UserData{ // strip secret
|
return &UserData{ // strip secret
|
||||||
Username: hau.Username,
|
Username: hau.Username,
|
||||||
|
AuthProvider: hau.AuthProvider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hau *HAUser) Provider() provider.AuthProvider {
|
||||||
|
return hau.AuthProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hau *UserData) Provider() provider.AuthProvider {
|
||||||
|
return hau.AuthProvider
|
||||||
|
}
|
||||||
|
|
||||||
type UserData struct {
|
type UserData struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
|
||||||
|
provider.AuthProvider `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ud *UserData) UserData() provider.ProviderUser {
|
func (ud *UserData) UserData() provider.ProviderUser {
|
||||||
|
@ -84,6 +95,28 @@ func (hap *HomeAssistantProvider) hashPass(p string) ([]byte, error) {
|
||||||
return bcrypt.GenerateFromPassword([]byte(p), bcrypt.DefaultCost)
|
return bcrypt.GenerateFromPassword([]byte(p), bcrypt.DefaultCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hap *HomeAssistantProvider) EqualCreds(c1, c2 provider.ProviderUser) bool {
|
||||||
|
switch c1c := c1.(type) {
|
||||||
|
case *HAUser:
|
||||||
|
switch c2c := c2.(type) {
|
||||||
|
case *HAUser:
|
||||||
|
return c2c.Username == c1c.Username
|
||||||
|
case *UserData:
|
||||||
|
return c2c.Username == c1c.Username
|
||||||
|
}
|
||||||
|
case *UserData:
|
||||||
|
switch c2c := c2.(type) {
|
||||||
|
case *HAUser:
|
||||||
|
return c2c.Username == c1c.Username
|
||||||
|
case *UserData:
|
||||||
|
return c2c.Username == c1c.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (hap *HomeAssistantProvider) ValidateCreds(r *http.Request, rm map[string]interface{}) (provider.ProviderUser, bool) {
|
func (hap *HomeAssistantProvider) ValidateCreds(r *http.Request, rm map[string]interface{}) (provider.ProviderUser, bool) {
|
||||||
usernameE, hasU := rm["username"]
|
usernameE, hasU := rm["username"]
|
||||||
passwordE, hasP := rm["password"]
|
passwordE, hasP := rm["password"]
|
||||||
|
@ -117,6 +150,7 @@ func (hap *HomeAssistantProvider) ValidateCreds(r *http.Request, rm map[string]i
|
||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword(hash, []byte(password))
|
err = bcrypt.CompareHashAndPassword(hash, []byte(password))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
found.AuthProvider = hap
|
||||||
return found, true
|
return found, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ type AuthProvider interface { // TODO: this should include stepping
|
||||||
FlowSchema() flow.Schema
|
FlowSchema() flow.Schema
|
||||||
NewCredData() interface{}
|
NewCredData() interface{}
|
||||||
ValidateCreds(r *http.Request, reqMap map[string]interface{}) (user ProviderUser, success bool)
|
ValidateCreds(r *http.Request, reqMap map[string]interface{}) (user ProviderUser, success bool)
|
||||||
|
EqualCreds(c1, c2 ProviderUser) bool
|
||||||
Lookup(ProviderUser) ProviderUser
|
Lookup(ProviderUser) ProviderUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,39 +25,10 @@ func Register(providerName string, f func(storage.Store) (AuthProvider, error))
|
||||||
Providers[providerName] = f
|
Providers[providerName] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
type Credentials struct {
|
|
||||||
ID CredID `json:"id"`
|
|
||||||
UserID UserID `json:"user_id"`
|
|
||||||
AuthProviderType string `json:"auth_provider_type"`
|
|
||||||
AuthProviderID *string `json:"auth_provider_id"`
|
|
||||||
DataRaw *json.RawMessage `json:"data,omitempty"`
|
|
||||||
user ProviderUser `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cred *Credentials) MarshalJSON() ([]byte, error) {
|
|
||||||
type CredAlias Credentials // alias so ø method set and we don't recurse
|
|
||||||
nCd := (*CredAlias)(cred)
|
|
||||||
|
|
||||||
if cred.user != nil {
|
|
||||||
providerData := cred.user.UserData()
|
|
||||||
if providerData != nil {
|
|
||||||
b, err := json.Marshal(providerData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dr := json.RawMessage(b)
|
|
||||||
nCd.DataRaw = &dr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Marshal(nCd)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProviderUser interface {
|
type ProviderUser interface {
|
||||||
// TODO: make sure this is sane with all the ProviderUser and UserData type stuff
|
// TODO: make sure this is sane with all the ProviderUser and UserData type stuff
|
||||||
UserData() ProviderUser
|
UserData() ProviderUser
|
||||||
Credentials() *Credentials
|
Provider() AuthProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthProviderBase struct {
|
type AuthProviderBase struct {
|
||||||
|
|
|
@ -18,12 +18,23 @@ type User struct {
|
||||||
|
|
||||||
func (hau *User) UserData() provider.ProviderUser {
|
func (hau *User) UserData() provider.ProviderUser {
|
||||||
return &UserData{
|
return &UserData{
|
||||||
UserID: hau.UserID,
|
UserID: hau.UserID,
|
||||||
|
AuthProvider: hau.AuthProvider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hau *UserData) Provider() provider.AuthProvider {
|
||||||
|
return hau.AuthProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hau *User) Provider() provider.AuthProvider {
|
||||||
|
return hau.AuthProvider
|
||||||
|
}
|
||||||
|
|
||||||
type UserData struct {
|
type UserData struct {
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
|
|
||||||
|
provider.AuthProvider `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ud *UserData) UserData() provider.ProviderUser {
|
func (ud *UserData) UserData() provider.ProviderUser {
|
||||||
|
@ -36,6 +47,11 @@ type TrustedNetworksProvider struct {
|
||||||
provider.AuthProviderBase `json:"-"`
|
provider.AuthProviderBase `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hap *TrustedNetworksProvider) EqualCreds(c1, c2 provider.ProviderUser) bool {
|
||||||
|
panic("not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func New(s storage.Store) (provider.AuthProvider, error) {
|
func New(s storage.Store) (provider.AuthProvider, error) {
|
||||||
hap := &TrustedNetworksProvider{
|
hap := &TrustedNetworksProvider{
|
||||||
AuthProviderBase: provider.AuthProviderBase{
|
AuthProviderBase: provider.AuthProviderBase{
|
||||||
|
|
|
@ -10,8 +10,6 @@ import (
|
||||||
"dynatron.me/x/blasphem/internal/common"
|
"dynatron.me/x/blasphem/internal/common"
|
||||||
"dynatron.me/x/blasphem/internal/generate"
|
"dynatron.me/x/blasphem/internal/generate"
|
||||||
"dynatron.me/x/blasphem/pkg/auth/provider"
|
"dynatron.me/x/blasphem/pkg/auth/provider"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -47,7 +45,7 @@ type authCodeStore struct {
|
||||||
|
|
||||||
type authCodeTuple struct {
|
type authCodeTuple struct {
|
||||||
ClientID ClientID
|
ClientID ClientID
|
||||||
Code AuthCode
|
Code AuthCode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *authCodeTuple) IsValid() bool {
|
func (t *authCodeTuple) IsValid() bool {
|
||||||
|
@ -90,7 +88,6 @@ func (ss *authCodeStore) cull() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *authCodeStore) store(clientID ClientID, cred *Credentials) string {
|
func (ss *authCodeStore) store(clientID ClientID, cred *Credentials) string {
|
||||||
log.Info().Msgf("store cred is %+v", cred)
|
|
||||||
ss.cull()
|
ss.cull()
|
||||||
code := generate.UUID()
|
code := generate.UUID()
|
||||||
ss.s[authCodeTuple{clientID, AuthCode(code)}] = flowResult{Time: time.Now(), Cred: cred}
|
ss.s[authCodeTuple{clientID, AuthCode(code)}] = flowResult{Time: time.Now(), Cred: cred}
|
||||||
|
@ -112,20 +109,21 @@ func (ss *authCodeStore) verify(tr *TokenRequest, r *http.Request) (*Credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
type Credentials struct {
|
type Credentials struct {
|
||||||
ID CredID `json:"id"`
|
ID CredID `json:"id"`
|
||||||
UserID UserID `json:"user_id"`
|
UserID UserID `json:"user_id"`
|
||||||
AuthProviderType string `json:"auth_provider_type"`
|
AuthProviderType string `json:"auth_provider_type"`
|
||||||
AuthProviderID *string `json:"auth_provider_id"`
|
AuthProviderID *string `json:"auth_provider_id"`
|
||||||
DataRaw *json.RawMessage `json:"data,omitempty"`
|
DataRaw *json.RawMessage `json:"data,omitempty"`
|
||||||
user provider.ProviderUser `json:"-"`
|
|
||||||
|
User provider.ProviderUser `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cred *Credentials) MarshalJSON() ([]byte, error) {
|
func (cred *Credentials) MarshalJSON() ([]byte, error) {
|
||||||
type CredAlias Credentials // alias so ø method set and we don't recurse
|
type CredAlias Credentials // alias so ø method set and we don't recurse
|
||||||
nCd := (*CredAlias)(cred)
|
nCd := (*CredAlias)(cred)
|
||||||
|
|
||||||
if cred.user != nil {
|
if cred.User != nil {
|
||||||
providerData := cred.user.UserData()
|
providerData := cred.User.UserData()
|
||||||
if providerData != nil {
|
if providerData != nil {
|
||||||
b, err := json.Marshal(providerData)
|
b, err := json.Marshal(providerData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -170,14 +168,15 @@ func (c *ClientID) IsValid() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthCode string
|
type AuthCode string
|
||||||
|
|
||||||
func (ac *AuthCode) IsValid() bool {
|
func (ac *AuthCode) IsValid() bool {
|
||||||
return *ac != ""
|
return *ac != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenRequest struct {
|
type TokenRequest struct {
|
||||||
ClientID ClientID `form:"client_id"`
|
ClientID ClientID `form:"client_id"`
|
||||||
Code AuthCode `form:"code"`
|
Code AuthCode `form:"code"`
|
||||||
GrantType GrantType `form:"grant_type"`
|
GrantType GrantType `form:"grant_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authenticator) TokenHandler(c echo.Context) error {
|
func (a *Authenticator) TokenHandler(c echo.Context) error {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"dynatron.me/x/blasphem/internal/generate"
|
||||||
"dynatron.me/x/blasphem/pkg/auth/provider"
|
"dynatron.me/x/blasphem/pkg/auth/provider"
|
||||||
"dynatron.me/x/blasphem/pkg/storage"
|
"dynatron.me/x/blasphem/pkg/storage"
|
||||||
|
|
||||||
|
@ -20,19 +21,40 @@ type AuthStore interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type authStore struct {
|
type authStore struct {
|
||||||
Users []User `json:"users"`
|
Users []*User `json:"users"`
|
||||||
Groups []Group `json:"groups"`
|
Groups []*Group `json:"groups"`
|
||||||
Credentials []Credentials `json:"credentials"`
|
Credentials []*Credentials `json:"credentials"`
|
||||||
Refresh []RefreshToken `json:"refresh_tokens"`
|
Refresh []*RefreshToken `json:"refresh_tokens"`
|
||||||
|
|
||||||
userMap map[UserID]*User
|
userMap map[UserID]*User
|
||||||
providerUsers map[provider.ProviderUser]*Credentials
|
providerUsers map[provider.ProviderUser]*Credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func strPtrEq(n1, n2 *string) bool {
|
||||||
|
return (n1 == n2 || (n1 != nil && n2 != nil && *n1 == *n2))
|
||||||
|
}
|
||||||
|
|
||||||
func (as *authStore) Credential(p provider.ProviderUser) *Credentials {
|
func (as *authStore) Credential(p provider.ProviderUser) *Credentials {
|
||||||
c, have := as.providerUsers[p]
|
for _, cr := range as.Credentials {
|
||||||
if !have {
|
if p.Provider() != nil &&
|
||||||
return nil
|
strPtrEq(cr.AuthProviderID, p.Provider().ProviderID()) &&
|
||||||
|
cr.AuthProviderType == p.Provider().ProviderType() &&
|
||||||
|
p.Provider().EqualCreds(cr.User.UserData(), p.UserData()) {
|
||||||
|
return cr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *authStore) newCredential(p provider.ProviderUser) *Credentials {
|
||||||
|
// XXX: probably broken
|
||||||
|
prov := p.Provider()
|
||||||
|
id := generate.UUID()
|
||||||
|
c := &Credentials{
|
||||||
|
ID: CredID(id),
|
||||||
|
AuthProviderType: prov.ProviderBase().Type,
|
||||||
|
AuthProviderID: prov.ProviderBase().ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
@ -46,7 +68,7 @@ func (a *Authenticator) newAuthStore(s storage.Store) (as *authStore, err error)
|
||||||
as.providerUsers = make(map[provider.ProviderUser]*Credentials)
|
as.providerUsers = make(map[provider.ProviderUser]*Credentials)
|
||||||
|
|
||||||
for _, u := range as.Users {
|
for _, u := range as.Users {
|
||||||
as.userMap[u.ID] = &u
|
as.userMap[u.ID] = u
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range as.Credentials {
|
for _, c := range as.Credentials {
|
||||||
|
@ -63,11 +85,11 @@ func (a *Authenticator) newAuthStore(s storage.Store) (as *authStore, err error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.user = prov.Lookup(pd.(provider.ProviderUser))
|
c.User = prov.Lookup(pd.(provider.ProviderUser))
|
||||||
if c.user == nil {
|
if c.User == nil {
|
||||||
return nil, fmt.Errorf("cannot find user in provider %s", prov.ProviderName())
|
return nil, fmt.Errorf("cannot find user in provider %s", prov.ProviderName())
|
||||||
}
|
}
|
||||||
as.providerUsers[c.user] = &c
|
as.providerUsers[c.User] = c
|
||||||
}
|
}
|
||||||
|
|
||||||
u, hasUser := as.userMap[c.UserID]
|
u, hasUser := as.userMap[c.UserID]
|
||||||
|
@ -90,7 +112,7 @@ func (a *Authenticator) newAuthStore(s storage.Store) (as *authStore, err error)
|
||||||
|
|
||||||
// don't leak memory
|
// don't leak memory
|
||||||
for j := i; j < len(as.Refresh); j++ {
|
for j := i; j < len(as.Refresh); j++ {
|
||||||
as.Refresh[j] = RefreshToken{}
|
as.Refresh[j] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
as.Refresh = as.Refresh[:i]
|
as.Refresh = as.Refresh[:i]
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import ()
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserID string
|
type UserID string
|
||||||
type GroupID string
|
type GroupID string
|
||||||
|
@ -19,7 +17,7 @@ type User struct {
|
||||||
Data interface{} `json:"data,omitempty"`
|
Data interface{} `json:"data,omitempty"`
|
||||||
UserMetadata
|
UserMetadata
|
||||||
|
|
||||||
Creds []Credentials `json:"-"`
|
Creds []*Credentials `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserMetadata struct {
|
type UserMetadata struct {
|
||||||
|
@ -39,7 +37,6 @@ func (u *User) allowedToAuth() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authenticator) getOrCreateUser(c *Credentials) (*User, error) {
|
func (a *Authenticator) getOrCreateUser(c *Credentials) (*User, error) {
|
||||||
log.Debug().Interface("userdata", c).Msg("getOrCreateUser")
|
|
||||||
u := a.store.User(c.UserID)
|
u := a.store.User(c.UserID)
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return nil, ErrInvalidAuth
|
return nil, ErrInvalidAuth
|
||||||
|
|
Loading…
Reference in a new issue