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
|
package auth
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"go-gentoo/pkg/config"
"go-gentoo/pkg/models"
"golang.org/x/oauth2"
"net/http"
"net/url"
)
func Login(w http.ResponseWriter, r *http.Request) {
b := make([]byte, 16)
rand.Read(b)
state := base64.URLEncoding.EncodeToString(b)
session, _ := CookieStore.Get(r, config.SessionStoreKey())
session.Values["state"] = state
session.Save(r, w)
url := Oauth2Config.AuthCodeURL(state)
http.Redirect(w, r, url, http.StatusFound)
}
func Callback(w http.ResponseWriter, r *http.Request) {
session, err := CookieStore.Get(r, config.SessionStoreKey())
if err != nil {
http.Error(w, "state did not match", http.StatusBadRequest)
return
}
if r.URL.Query().Get("state") != session.Values["state"] {
http.Error(w, "state did not match", http.StatusBadRequest)
return
}
oauth2Token, err := Oauth2Config.Exchange(Ctx, r.URL.Query().Get("code"))
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError)
return
}
idToken, err := Verifier.Verify(Ctx, rawIDToken)
if err != nil {
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
return
}
resp := struct {
OAuth2Token *oauth2.Token
IDTokenClaims *json.RawMessage // ID Token payload is just JSON.
}{oauth2Token, new(json.RawMessage)}
if err := idToken.Claims(&resp.IDTokenClaims); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
type Response struct {
GivenName string `json:"given_name"`
Username string `json:"preferred_username"`
Email string `json:"email"`
}
var keycloakResponse Response
err = json.Unmarshal(*resp.IDTokenClaims, &keycloakResponse)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Values["idToken"] = rawIDToken
session.Values["user"] = models.User{
Email: keycloakResponse.Email,
RealName: keycloakResponse.GivenName,
UserName: keycloakResponse.Username,
Projects: nil,
}
err = session.Save(r, w)
http.Redirect(w, r, "/", http.StatusFound)
}
// http://www.gorillatoolkit.org/pkg/sessions#CookieStore.MaxAge
func Logout(w http.ResponseWriter, r *http.Request) {
session, err := CookieStore.Get(r, config.SessionStoreKey())
if err != nil {
return
}
session.Options.MaxAge = -1
session.Save(r, w)
http.Redirect(w, r, config.OIDConfigURL()+"/protocol/openid-connect/logout?redirect_uri="+url.QueryEscape(config.ApplicationURL()), 302)
}
|