Skip to content

Commit d976ff3

Browse files
authored
Merge pull request #20 from jferrl/dev/jorge/14
feat!: add Go generics support for unified App ID/Client ID authentication
2 parents 2e9506a + a3f1c65 commit d976ff3

File tree

5 files changed

+340
-77
lines changed

5 files changed

+340
-77
lines changed

README.md

Lines changed: 163 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,30 @@
77

88
`go-githubauth` is a Go package that provides utilities for GitHub authentication, including generating and using GitHub App tokens and installation tokens.
99

10+
**v2.0.0** introduces Go generics support for unified authentication with both numeric App IDs and alphanumeric Client IDs in a single, type-safe API.
11+
1012
## Features
1113

1214
`go-githubauth` package provides implementations of the `TokenSource` interface from the `golang.org/x/oauth2` package. This interface has a single method, Token, which returns an *oauth2.Token.
1315

16+
### v2.0.0 Features
17+
18+
- **🔥 Go Generics Support**: Single `NewApplicationTokenSource` function supports both `int64` App IDs and `string` Client IDs
19+
- **🛡️ Type Safety**: Compile-time verification of identifier types through generic constraints
20+
- **⚡ Type Inference**: Automatic type detection - no need to specify generic parameters explicitly
21+
- **📖 Enhanced Documentation**: Official GitHub API references and comprehensive JWT details
22+
23+
### Core Capabilities
24+
1425
- Generate GitHub Application JWT [Generating a jwt for a github app](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app)
1526
- Obtain GitHub App installation tokens [Authenticating as a GitHub App](https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api?apiVersion=2022-11-28#authenticating-with-a-token-generated-by-an-app)
27+
- RS256-signed JWTs with proper clock drift protection
28+
- Support for both legacy App IDs and modern Client IDs (recommended by GitHub)
29+
30+
### Requirements
1631

17-
This package is designed to be used with the `golang.org/x/oauth2` package, which provides support for OAuth2 authentication.
32+
- **Go 1.18+** (required for generics support)
33+
- This package is designed to be used with the `golang.org/x/oauth2` package
1834

1935
## Installation
2036

@@ -26,7 +42,9 @@ go get -u github.com/jferrl/go-githubauth
2642

2743
## Usage
2844

29-
### Usage with [go-github](https://github.com/google/go-github) and [oauth2](golang.org/x/oauth2)
45+
### Usage with [go-github](https://github.com/google/go-github) and [oauth2](golang.org/x/oauth2)
46+
47+
#### Client ID (Recommended)
3048

3149
```go
3250
package main
@@ -37,30 +55,70 @@ import (
3755
"os"
3856
"strconv"
3957

40-
"github.com/google/go-github/v62/github"
58+
"github.com/google/go-github/v73/github"
4159
"github.com/jferrl/go-githubauth"
4260
"golang.org/x/oauth2"
4361
)
4462

4563
func main() {
4664
privateKey := []byte(os.Getenv("GITHUB_APP_PRIVATE_KEY"))
47-
appID, _ := strconv.ParseInt(os.Getenv("GITHUB_APP_ID"), 10, 64)
65+
clientID := os.Getenv("GITHUB_APP_CLIENT_ID") // e.g., "Iv1.1234567890abcdef"
4866
installationID, _ := strconv.ParseInt(os.Getenv("GITHUB_INSTALLATION_ID"), 10, 64)
4967

50-
appTokenSource, err := githubauth.NewApplicationTokenSource(appID, privateKey)
68+
// Go automatically infers the type as string for Client ID
69+
appTokenSource, err := githubauth.NewApplicationTokenSource(clientID, privateKey)
5170
if err != nil {
5271
fmt.Println("Error creating application token source:", err)
5372
return
5473
}
5574

5675
installationTokenSource := githubauth.NewInstallationTokenSource(installationID, appTokenSource)
5776

58-
// oauth2.NewClient create a new http.Client that adds an Authorization header with the token.
59-
// Transport src use oauth2.ReuseTokenSource to reuse the token.
60-
// The token will be reused until it expires.
61-
// The token will be refreshed if it's expired.
77+
// oauth2.NewClient creates a new http.Client that adds an Authorization header with the token
6278
httpClient := oauth2.NewClient(context.Background(), installationTokenSource)
79+
githubClient := github.NewClient(httpClient)
80+
81+
_, _, err = githubClient.PullRequests.CreateComment(context.Background(), "owner", "repo", 1, &github.PullRequestComment{
82+
Body: github.String("Awesome comment!"),
83+
})
84+
if err != nil {
85+
fmt.Println("Error creating comment:", err)
86+
return
87+
}
88+
}
89+
```
90+
91+
#### App ID (Legacy)
92+
93+
```go
94+
package main
95+
96+
import (
97+
"context"
98+
"fmt"
99+
"os"
100+
"strconv"
101+
102+
"github.com/google/go-github/v73/github"
103+
"github.com/jferrl/go-githubauth"
104+
"golang.org/x/oauth2"
105+
)
106+
107+
func main() {
108+
privateKey := []byte(os.Getenv("GITHUB_APP_PRIVATE_KEY"))
109+
appID, _ := strconv.ParseInt(os.Getenv("GITHUB_APP_ID"), 10, 64)
110+
installationID, _ := strconv.ParseInt(os.Getenv("GITHUB_INSTALLATION_ID"), 10, 64)
111+
112+
// Explicitly cast to int64 for App ID - Go automatically infers the type
113+
appTokenSource, err := githubauth.NewApplicationTokenSource(int64(appID), privateKey)
114+
if err != nil {
115+
fmt.Println("Error creating application token source:", err)
116+
return
117+
}
63118

119+
installationTokenSource := githubauth.NewInstallationTokenSource(installationID, appTokenSource)
120+
121+
httpClient := oauth2.NewClient(context.Background(), installationTokenSource)
64122
githubClient := github.NewClient(httpClient)
65123

66124
_, _, err = githubClient.PullRequests.CreateComment(context.Background(), "owner", "repo", 1, &github.PullRequestComment{
@@ -75,9 +133,9 @@ func main() {
75133

76134
### Generate GitHub Application Token
77135

78-
First of all you need to create a GitHub App and generate a private key.
136+
First, create a GitHub App and generate a private key. To authenticate as a GitHub App, you need to generate a JWT. [Generating a JWT for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app)
79137

80-
To authenticate as a GitHub App, you need to generate a JWT. [Generating a jwt for a github app](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app)
138+
#### With Client ID (Recommended)
81139

82140
```go
83141
package main
@@ -90,11 +148,55 @@ import (
90148
"github.com/jferrl/go-githubauth"
91149
)
92150

151+
func main() {
152+
privateKey := []byte(os.Getenv("GITHUB_APP_PRIVATE_KEY"))
153+
clientID := os.Getenv("GITHUB_APP_CLIENT_ID") // e.g., "Iv1.1234567890abcdef"
154+
155+
// Type automatically inferred as string
156+
tokenSource, err := githubauth.NewApplicationTokenSource(
157+
clientID,
158+
privateKey,
159+
githubauth.WithApplicationTokenExpiration(5*time.Minute),
160+
)
161+
if err != nil {
162+
fmt.Println("Error creating token source:", err)
163+
return
164+
}
165+
166+
token, err := tokenSource.Token()
167+
if err != nil {
168+
fmt.Println("Error generating token:", err)
169+
return
170+
}
171+
172+
fmt.Println("Generated JWT token:", token.AccessToken)
173+
}
174+
```
175+
176+
#### With App ID
177+
178+
```go
179+
package main
180+
181+
import (
182+
"fmt"
183+
"os"
184+
"strconv"
185+
"time"
186+
187+
"github.com/jferrl/go-githubauth"
188+
)
189+
93190
func main() {
94191
privateKey := []byte(os.Getenv("GITHUB_APP_PRIVATE_KEY"))
95192
appID, _ := strconv.ParseInt(os.Getenv("GITHUB_APP_ID"), 10, 64)
96193

97-
tokenSource, err := githubauth.NewApplicationTokenSource(appID, privateKey, githubauth.WithApplicationTokenExpiration(5*time.Minute))
194+
// Type automatically inferred as int64
195+
tokenSource, err := githubauth.NewApplicationTokenSource(
196+
int64(appID),
197+
privateKey,
198+
githubauth.WithApplicationTokenExpiration(5*time.Minute),
199+
)
98200
if err != nil {
99201
fmt.Println("Error creating token source:", err)
100202
return
@@ -106,13 +208,13 @@ func main() {
106208
return
107209
}
108210

109-
fmt.Println("Generated token:", token.AccessToken)
211+
fmt.Println("Generated JWT token:", token.AccessToken)
110212
}
111213
```
112214

113215
### Generate GitHub App Installation Token
114216

115-
To authenticate as a GitHub App installation, you need to obtain an installation token.
217+
To authenticate as a GitHub App installation, you need to obtain an installation token using your GitHub App JWT.
116218

117219
```go
118220
package main
@@ -127,15 +229,17 @@ import (
127229

128230
func main() {
129231
privateKey := []byte(os.Getenv("GITHUB_APP_PRIVATE_KEY"))
130-
appID, _ := strconv.ParseInt(os.Getenv("GITHUB_APP_ID"), 10, 64)
232+
clientID := os.Getenv("GITHUB_APP_CLIENT_ID") // e.g., "Iv1.1234567890abcdef"
131233
installationID, _ := strconv.ParseInt(os.Getenv("GITHUB_INSTALLATION_ID"), 10, 64)
132234

133-
appTokenSource, err := githubauth.NewApplicationTokenSource(appID, privateKey)
235+
// Create GitHub App JWT token source with Client ID
236+
appTokenSource, err := githubauth.NewApplicationTokenSource(clientID, privateKey)
134237
if err != nil {
135238
fmt.Println("Error creating application token source:", err)
136239
return
137240
}
138241

242+
// Create installation token source using the app token source
139243
installationTokenSource := githubauth.NewInstallationTokenSource(installationID, appTokenSource)
140244

141245
token, err := installationTokenSource.Token()
@@ -148,6 +252,49 @@ func main() {
148252
}
149253
```
150254

255+
## Migration from v1.x to v2.0.0
256+
257+
v2.0.0 introduces breaking changes with Go generics support. Here's how to migrate:
258+
259+
### ⚠️ Breaking Changes
260+
261+
#### Removed Functions
262+
263+
-`NewApplicationTokenSource(int64, []byte, ...opts)`
264+
265+
#### New Unified Function
266+
267+
-`NewApplicationTokenSource[T Identifier](T, []byte, ...opts)`
268+
269+
### 🔧 Migration Guide
270+
271+
#### Before (v1.x)
272+
273+
```go
274+
// Using App ID
275+
tokenSource1, err := githubauth.NewApplicationTokenSource(12345, privateKey)
276+
277+
// Using Client ID
278+
tokenSource2, err := githubauth.NewApplicationTokenSourceWithClientID("Iv1.abc123", privateKey)
279+
```
280+
281+
#### After (v2.0.0)
282+
283+
```go
284+
// Using App ID - explicit int64 cast needed for type inference
285+
tokenSource1, err := githubauth.NewApplicationTokenSource(int64(12345), privateKey)
286+
287+
// Using Client ID - works directly
288+
tokenSource2, err := githubauth.NewApplicationTokenSource("Iv1.abc123", privateKey)
289+
```
290+
291+
### ✨ Benefits of Migration
292+
293+
- **Type Safety**: Compile-time verification of identifier types
294+
- **Code Consistency**: Single function for all authentication types
295+
- **Future-Proof**: Ready for potential new GitHub identifier formats
296+
- **Enhanced Documentation**: Better godoc with GitHub API references
297+
151298
## Contributing
152299

153300
Contributions are welcome! Please open an issue or submit a pull request on GitHub.

0 commit comments

Comments
 (0)