forked from liam-middlebrook/csh-auth
/
csh_auth.go
162 lines (143 loc) · 4.31 KB
/
csh_auth.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
152
153
154
155
156
157
158
159
160
161
162
package csh_auth
import (
"errors"
"net/http"
"time"
oidc "github.com/coreos/go-oidc"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"golang.org/x/oauth2"
)
const AuthKey = "cshauth"
const CookieName = "Auth"
const ProviderURI = "https://sso.csh.rit.edu/auth/realms/csh"
// =================
// structs
// =================
type CSHAuth struct {
clientID string
clientSecret string
secret string
state string
server_host string
redirect_uri string
authenticate_uri string
config oauth2.Config // this guy changes a bit, weird
ctx context.Context
provider *oidc.Provider
}
type CSHClaims struct {
Token string `json:"token"`
UserInfo CSHUserInfo `"json:user_info"`
jwt.StandardClaims
}
type CSHUserInfo struct {
Subject string `json:"uuid"`
Profile string `json:"profile"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
// contains filtered or unexported fields
Username string `json:"preferred_username"`
FullName string `json:"name"`
Groups []string `json:"groups"`
}
// =================
// auth helper
// =================
func (auth *CSHAuth) AuthWrapper(page gin.HandlerFunc) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
cookie, err := c.Cookie(CookieName)
if err != nil || cookie == "" {
log.Info("cookie not found")
c.Redirect(http.StatusFound, auth.authenticate_uri+"?referer="+c.Request.URL.String())
return
}
token, err := jwt.ParseWithClaims(cookie, &CSHClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(auth.secret), nil
})
if err != nil {
log.Error("token failure")
return
}
if claims, ok := token.Claims.(*CSHClaims); ok && token.Valid {
// add in user info data
c.Set(AuthKey, *claims)
// call the wrapped func
page(c)
} else {
log.Error("claim parsing failure")
}
})
}
func (auth *CSHAuth) AuthRequest(c *gin.Context) {
// Thrash this so we don't get additive weirdness
auth.config.RedirectURL = auth.redirect_uri + "?referer=" + c.Query("referer")
c.Redirect(http.StatusFound, auth.config.AuthCodeURL(auth.state))
}
func (auth *CSHAuth) AuthCallback(c *gin.Context) {
if c.Query("state") != auth.state {
log.Error("state does not match")
return
}
oauth2Token, err := auth.config.Exchange(auth.ctx, c.Query("code"))
if err != nil {
log.Error("failed to exchange token")
return
}
userInfo := &CSHUserInfo{}
oidcUserInfo, err := auth.provider.UserInfo(auth.ctx, oauth2.StaticTokenSource(oauth2Token))
if err != nil {
log.Error("failed to get userinfo")
}
oidcUserInfo.Claims(userInfo)
if err != nil {
log.Error("failed to marshal userinfo")
}
expireToken := time.Now().Add(time.Hour * 1).Unix()
expireCookie := 3600
claims := CSHClaims{
oauth2Token.AccessToken,
*userInfo,
jwt.StandardClaims{
ExpiresAt: expireToken,
Issuer: auth.server_host,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString([]byte(auth.secret))
c.SetCookie(CookieName, signedToken, int(expireCookie), "", "", false, true)
c.Redirect(http.StatusFound, c.Query("referer"))
}
func (auth *CSHAuth) Init(clientID, clientSecret, secret, state, server_host, redirect_uri, auth_uri string, scopes []string) {
auth.clientID = clientID
auth.clientSecret = clientSecret
auth.secret = secret
auth.state = state
auth.server_host = server_host
auth.redirect_uri = redirect_uri
auth.authenticate_uri = auth_uri
var err error
auth.ctx = context.Background()
auth.provider, err = oidc.NewProvider(auth.ctx, ProviderURI)
if err != nil {
log.Error("Failed to Create oidc Provider")
}
copy(scopes[:], []string{oidc.ScopeOpenID}[:])
log.Info(auth.authenticate_uri)
auth.config = oauth2.Config{
ClientID: auth.clientID,
ClientSecret: auth.clientSecret,
Endpoint: auth.provider.Endpoint(),
RedirectURL: auth.redirect_uri,
Scopes: scopes,
}
}
func (auth *CSHAuth) AuthLogout(c *gin.Context) {
c.SetCookie(CookieName, "", 0, "", "", false, true)
c.Redirect(http.StatusFound, ProviderURI+"/protocol/openid-connect/logout?redirect_uri="+auth.server_host+"/")
}