summaryrefslogtreecommitdiffstats
path: root/services/auth/source/oauth2/token.go
blob: 3405619d3fa59abbf565c4f2f2171bc733615583 (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
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package oauth2

import (
	"fmt"
	"time"

	"code.gitea.io/gitea/modules/timeutil"

	"github.com/golang-jwt/jwt/v5"
)

// ___________     __
// \__    ___/___ |  | __ ____   ____
//   |    | /  _ \|  |/ // __ \ /    \
//   |    |(  <_> )    <\  ___/|   |  \
//   |____| \____/|__|_ \\___  >___|  /
//                     \/    \/     \/

// Token represents an Oauth grant

// TokenType represents the type of token for an oauth application
type TokenType int

const (
	// TypeAccessToken is a token with short lifetime to access the api
	TypeAccessToken TokenType = 0
	// TypeRefreshToken is token with long lifetime to refresh access tokens obtained by the client
	TypeRefreshToken = iota
)

// Token represents a JWT token used to authenticate a client
type Token struct {
	GrantID int64     `json:"gnt"`
	Type    TokenType `json:"tt"`
	Counter int64     `json:"cnt,omitempty"`
	jwt.RegisteredClaims
}

// ParseToken parses a signed jwt string
func ParseToken(jwtToken string, signingKey JWTSigningKey) (*Token, error) {
	parsedToken, err := jwt.ParseWithClaims(jwtToken, &Token{}, func(token *jwt.Token) (any, error) {
		if token.Method == nil || token.Method.Alg() != signingKey.SigningMethod().Alg() {
			return nil, fmt.Errorf("unexpected signing algo: %v", token.Header["alg"])
		}
		return signingKey.VerifyKey(), nil
	})
	if err != nil {
		return nil, err
	}
	if !parsedToken.Valid {
		return nil, fmt.Errorf("invalid token")
	}
	var token *Token
	var ok bool
	if token, ok = parsedToken.Claims.(*Token); !ok || !parsedToken.Valid {
		return nil, fmt.Errorf("invalid token")
	}
	return token, nil
}

// SignToken signs the token with the JWT secret
func (token *Token) SignToken(signingKey JWTSigningKey) (string, error) {
	token.IssuedAt = jwt.NewNumericDate(time.Now())
	jwtToken := jwt.NewWithClaims(signingKey.SigningMethod(), token)
	signingKey.PreProcessToken(jwtToken)
	return jwtToken.SignedString(signingKey.SignKey())
}

// OIDCToken represents an OpenID Connect id_token
type OIDCToken struct {
	jwt.RegisteredClaims
	Nonce string `json:"nonce,omitempty"`

	// Scope profile
	Name              string             `json:"name,omitempty"`
	PreferredUsername string             `json:"preferred_username,omitempty"`
	Profile           string             `json:"profile,omitempty"`
	Picture           string             `json:"picture,omitempty"`
	Website           string             `json:"website,omitempty"`
	Locale            string             `json:"locale,omitempty"`
	UpdatedAt         timeutil.TimeStamp `json:"updated_at,omitempty"`

	// Scope email
	Email         string `json:"email,omitempty"`
	EmailVerified bool   `json:"email_verified,omitempty"`

	// Groups are generated by organization and team names
	Groups []string `json:"groups,omitempty"`
}

// SignToken signs an id_token with the (symmetric) client secret key
func (token *OIDCToken) SignToken(signingKey JWTSigningKey) (string, error) {
	token.IssuedAt = jwt.NewNumericDate(time.Now())
	jwtToken := jwt.NewWithClaims(signingKey.SigningMethod(), token)
	signingKey.PreProcessToken(jwtToken)
	return jwtToken.SignedString(signingKey.SignKey())
}