summaryrefslogtreecommitdiff
blob: 8e79959890a003122249daf4e4183e559337a632 (plain)
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)
}