Commit 36178298a2935988b79ace86fbba96d355dd6dce

Parents: e0747184abdd76cc8f9e2abf529fbb618e33544f

From: Moritz Poldrack <git@moritz.sh>
Date: Thu Jan 11 20:21:40 2024 +0700

add logout URL generation

		

Stats

configuration.go +5/-0
token.go +19/-3

Changeset

 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
diff --git a/configuration.go b/configuration.go
index 2ad1121e38799983294f06245b431e7f04e77675..1a9bf7c13a26612c16fbe9e7dfc8803f05c0eb15 100644
--- a/configuration.go
+++ b/configuration.go
@@ -121,3 +121,8 @@ 	}
 	cfg.scopes = scopes
 	return nil
 }
+
+// LogoutURL returns the generic logout URL
+func (cfg *Configuration) LogoutURL() string {
+	return cfg.cfg.EndSessionEndpoint
+}
diff --git a/token.go b/token.go
index 6aa9486348196d36f6baee58670bc733aaa743cc..8261c0eb5df9054038e06b47b7ee61c4430e86b3 100644
--- a/token.go
+++ b/token.go
@@ -6,6 +6,7 @@
 import (
 	"context"
 	"fmt"
+	"net/url"
 	"time"
 
 	"github.com/lestrrat-go/jwx/v2/jwk"
@@ -44,9 +45,10 @@ )
 
 // Token returns an OIDC token returned by the service
 type Token struct {
-	source  oauth2.TokenSource
-	idToken openid.Token
-	cfg     *Configuration
+	source          oauth2.TokenSource
+	idToken         openid.Token
+	cfg             *Configuration
+	originalIDToken string
 }
 
 func (cfg *Configuration) newToken(tok *oauth2.Token) *Token {
@@ -56,6 +58,7 @@ 		cfg:    cfg,
 	}
 
 	token := fmt.Sprint(tok.Extra("id_token"))
+	result.originalIDToken = token
 	var keyset jwk.Set
 	var err error
 	if !cfg.options.has(OptionSkipTokenValidation) {
@@ -114,6 +117,7 @@ 		return result, fmt.Errorf("provided token is not an OIDC token")
 	}
 
 	result.idToken = oidToken
+	result.originalIDToken = token
 	result.source = oauth2.StaticTokenSource(&oauth2.Token{
 		AccessToken: token,
 		Expiry:      oidToken.Expiration(),
@@ -132,3 +136,15 @@ func (t *Token) Valid() bool {
 	exp := t.Expiration()
 	return exp.After(time.Now())
 }
+
+// LogoutURL returns the logout URL to invalidate the token
+func (t *Token) LogoutURL() string {
+	u, err := url.Parse(t.cfg.LogoutURL())
+	if err != nil {
+		return ""
+	}
+	q := u.Query()
+	q.Set("id_token_hint", t.originalIDToken)
+	u.RawQuery = q.Encode()
+	return u.String()
+}