-
Notifications
You must be signed in to change notification settings - Fork 0
/
authorization.go
151 lines (120 loc) · 3.74 KB
/
authorization.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package auth
import (
"context"
"encoding/base64"
"fmt"
"time"
"github.com/google/uuid"
"github.com/seb-schulz/onegate/internal/database"
"github.com/seb-schulz/onegate/internal/model"
"github.com/seb-schulz/onegate/internal/sessionmgr"
)
type authorizationCodeChallenger interface {
CodeChallenge() string
}
type authorization interface {
ClientID() uuid.UUID
UserID() uint
State() string
Code() string
authorizationCodeChallenger
redirecter
SetUserID(context.Context, uint) error
Delete(context.Context) error
}
type Authorization struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
InternalClientID uuid.UUID `gorm:"column:client_id;type:VARCHAR;size:191;not null"`
Client Client `gorm:"foreignKey:InternalClientID"`
InternalUserID *uint `gorm:"column:user_id"`
User *model.User `gorm:"foreignKey:InternalUserID"`
InternalState string `gorm:"column:state"`
InternalCode []byte `gorm:"column:code;type:BLOB(16)"`
InternalCodeChallenge string `gorm:"column:code_challenge;type:BLOB(16)"`
SessionID uuid.UUID `gorm:"column:session_id;type:VARCHAR(191);not null"`
}
func (a *Authorization) ClientID() uuid.UUID {
return a.InternalClientID
}
func (a *Authorization) UserID() uint {
if a.InternalUserID == nil {
return 0
}
return *a.InternalUserID
}
func (a *Authorization) State() string {
return a.InternalState
}
func (a *Authorization) Code() string {
return base64.URLEncoding.EncodeToString(a.InternalCode)
}
func (a *Authorization) CodeChallenge() string {
return a.InternalCodeChallenge
}
func (a *Authorization) RedirectURI() string {
return a.Client.RedirectURI()
}
func (a *Authorization) IDStr() string {
return fmt.Sprint(a.ID)
}
func (a *Authorization) SetUserID(ctx context.Context, userID uint) error {
r := database.FromContext(ctx).Model(a).Update("user_id", userID)
if r.Error != nil {
return fmt.Errorf("cannot update authorization: %w", r.Error)
}
return nil
}
func (a *Authorization) Delete(ctx context.Context) error {
r := database.FromContext(ctx).Delete(&a)
if r.Error != nil {
return fmt.Errorf("cannot delete authorization: %v", r.Error)
}
return nil
}
func createAuthorization(ctx context.Context, client client, state, codeChallenge string) error {
if state == "" {
return fmt.Errorf("state must not be empty")
}
if codeChallenge == "" {
return fmt.Errorf("code challenge must not be empty")
}
code := make([]byte, 16)
if err := readRand(code); err != nil {
panic("cannot generate code")
}
authReq := Authorization{
InternalClientID: client.ClientID(),
InternalState: state,
InternalCodeChallenge: codeChallenge,
InternalCode: code,
SessionID: sessionmgr.FromContext(ctx).UUID,
}
r := database.FromContext(ctx).Create(&authReq)
if r.Error != nil {
return fmt.Errorf("cannot create authorization: %v", r.Error)
}
return nil
}
func FirstAuthorization(ctx context.Context) (*Authorization, error) {
sessionID := sessionmgr.FromContext(ctx).UUID
authReq := Authorization{}
r := database.FromContext(ctx).Preload("Client").Where("session_id = ?", sessionID).First(&authReq)
if r.Error != nil {
return nil, fmt.Errorf("cannot update authorization: %v", r.Error)
}
return &authReq, nil
}
func authorizationByCode(ctx context.Context, code string) (authorization, error) {
decCode, err := base64.URLEncoding.DecodeString(code)
if err != nil {
return nil, err
}
authReq := Authorization{}
r := database.FromContext(ctx).Where("code = ?", decCode).First(&authReq)
if r.Error != nil {
return nil, fmt.Errorf("cannot get authorization: %w", r.Error)
}
return &authReq, nil
}