Skip to content

Refactor JWT token handling and enhance tests - #29

Merged
evgenyk merged 2 commits into
mainfrom
ev/token_methods
Aug 26, 2025
Merged

Refactor JWT token handling and enhance tests#29
evgenyk merged 2 commits into
mainfrom
ev/token_methods

Conversation

@evgenyk

@evgenyk evgenyk commented Aug 26, 2025

Copy link
Copy Markdown
Contributor

Refactor methods for retrieving JWT claims to improve clarity and functionality. Add comprehensive tests to ensure correct behavior and edge case handling. Update documentation for better understanding of token claims.

- Introduce new methods to retrieve claims such as issuer, audience, expiration, issued at, JWT ID, permissions, scopes, organization code, authorized party, and feature flags.
- Update existing methods for better clarity and functionality.
- Add comprehensive tests for new and existing methods to ensure correct behavior and handling of edge cases.
- Improve documentation for methods related to token claims.
@coderabbitai

coderabbitai Bot commented Aug 26, 2025

Copy link
Copy Markdown

Walkthrough

Adds numerous JWT Token accessor methods, a FeatureFlag model, AsString and validation-error accessors in jwt/token.go; expands tests in jwt/jwt_test.go to cover claim extraction, OAuth2 token fields, feature flags, and serialization; updates top-level README JWT docs and adds a new jwt/README.md with usage and validation guidance.

Changes

Cohort / File(s) Summary
Token API enhancements
jwt/token.go
Adds AsString, GetClaims, GetValidationErrors, GetIssuer, GetAudience, GetExpiration, GetIssuedAt, GetJWTID, GetPermissions, GetScopes, GetOrganizationCode, GetAuthorizedParty, GetSubject updates, FeatureFlag type, GetFeatureFlags, GetFeatureFlag*, and internal helpers for claim normalization.
Test suite expansion
jwt/jwt_test.go
Replaces prior single claims test with extensive tests covering all new accessors, numeric and array claim handling, feature flag parsing and typed accessors, OAuth2 raw/id/access/refresh token getters, AsString/ToString behavior, and test helpers (testJwtToken, testPublicPEM).
Top-level README updates
README.md
Reworks JWT documentation: renames section header to "JWT Package", removes the detailed API table and replaces it with a concise overview pointing to jwt/README.md and listing high-level capabilities.
New package README
jwt/README.md
Adds a comprehensive JWT package README describing installation, quick start, parsing methods, validation options (JWKS, public key, custom key func, alg/aud/iss/claims/time), token accessors (IsValid, GetRawToken, GetSubject, GetIssuer, GetAudience, GetClaims, GetAccessToken, GetIdToken, GetRefreshToken, AsString, GetValidationErrors), use cases, and examples.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller
  participant T as Token
  participant P as JWT Parser
  participant CMs as Claims (Map)
  participant FF as Feature Flags

  C->>T: New Token(raw)
  T->>P: Parse raw JWT
  P-->>T: Parsed token or error
  T-->>C: Token (with validation errors if any)

  rect rgba(230,245,255,0.5)
  note over C,T: Access standard claims
  C->>T: GetIssuer / GetSubject / GetAudience / GetExpiration / GetIssuedAt / GetJWTID
  T->>CMs: Read claim(s)
  CMs-->>T: Values (string/number/array)
  T-->>C: Normalized values
  end

  rect rgba(240,255,230,0.5)
  note over C,T: Feature flags access
  C->>T: GetFeatureFlags / GetFeatureFlagXxx(name)
  T->>CMs: Read feature_flags
  CMs-->>T: map[string]{t,v}
  T->>FF: Normalize FeatureFlag
  FF-->>T: Typed value (bool/string/int)
  T-->>C: Result (+ok)
  end

  rect rgba(255,245,230,0.5)
  note over C,T: Serialization and errors
  C->>T: AsString()
  T-->>C: JSON string or error
  C->>T: GetValidationErrors()
  T-->>C: error (if any)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ev/token_methods

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
jwt/token.go (1)

339-341: Ensure GetValidationErrors returns nil when there are no errors

The current implementation of GetValidationErrors always calls newError, which in turn invokes fmt.Errorf with a non-empty format string and the nil error (and no additional errors). Because Go’s fmt.Errorf treats %w exactly like %v—and %v prints <nil> rather than returning a nil error—newError will always produce a non-nil error value even when j.validationErrors is empty (go.dev, exchangetuts.com).

To avoid “manufacturing” an error when there truly are no validation errors, add an explicit nil-check:

• File jwt/token.go, lines 339–341:

 func (j *Token) GetValidationErrors() error {
-	return newError("token validation errors", nil, j.validationErrors...)
+	if len(j.validationErrors) == 0 {
+		return nil
+	}
+	return newError("token validation errors", nil, j.validationErrors...)
 }

This change ensures that consumers of GetValidationErrors can simply write:

if err := token.GetValidationErrors(); err != nil {
    // handle real validation error(s)
}

without being misled by a spurious, empty-error wrapper.

🧹 Nitpick comments (15)
jwt/token.go (10)

42-49: Document and safeguard JSON serialization of oauth2.Token (PII exposure and missing extras).

AsString marshals the oauth2.Token directly. Two caveats:

  • Sensitive fields (access/refresh tokens) will be serialized as-is; easy to leak in logs.
  • oauth2.Token.WithExtra data (e.g., id_token) lives in unexported fields and won’t appear in the JSON, which may surprise callers.

Recommend clarifying the docstring and optionally providing a redacted variant (e.g., AsRedactedString) to avoid accidental leakage.

Apply this doc tweak:

-// AsString returns the token as a JSON string.
+// AsString returns the oauth2.Token as a JSON string.
+// Note:
+// - Sensitive fields like AccessToken and RefreshToken are included verbatim.
+// - Values added via oauth2.Token.WithExtra (e.g., "id_token") are stored in unexported fields
+//   and will not be present in the JSON output.
 func (j *Token) AsString() (string, error) {
   marshalledToken, err := json.Marshal(j.rawToken)
   if err != nil {
     return "", err
   }
   return string(marshalledToken), nil
 }

Optionally add a safe alternative (outside this hunk):

// AsRedactedString returns a JSON string with sensitive fields masked.
func (j *Token) AsRedactedString() (string, error) {
	if j.rawToken == nil {
		return "null", nil
	}
	redacted := *j.rawToken
	if redacted.AccessToken != "" {
		redacted.AccessToken = "****"
	}
	if redacted.RefreshToken != "" {
		redacted.RefreshToken = "****"
	}
	b, err := json.Marshal(redacted)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

65-79: Prefer jwt/v5 helper over manual MapClaims cast for iss.

Use Claims.GetIssuer() to support both MapClaims and RegisteredClaims and avoid map-typing boilerplate.

-// GetIssuer returns the iss claim of the token.
-func (j *Token) GetIssuer() string {
-	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
-		return ""
-	}
-	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		if issuer, exists := claims["iss"]; exists {
-			if issuerStr, ok := issuer.(string); ok {
-				return issuerStr
-			}
-		}
-	}
-	return ""
-}
+// GetIssuer returns the iss claim of the token.
+func (j *Token) GetIssuer() string {
+	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
+		return ""
+	}
+	iss, _ := j.processing.parsed.Claims.GetIssuer()
+	return iss
+}

80-103: Simplify aud parsing with jwt/v5 API and cover more shapes.

Use Claims.GetAudience() which normalizes string/array forms into ClaimStrings.

-// GetAudience returns the aud claim of the token.
-func (j *Token) GetAudience() []string {
-	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
-		return nil
-	}
-	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		if audience, exists := claims["aud"]; exists {
-			switch aud := audience.(type) {
-			case string:
-				return []string{aud}
-			case []interface{}:
-				result := make([]string, 0, len(aud))
-				for _, a := range aud {
-					if aStr, ok := a.(string); ok {
-						result = append(result, aStr)
-					}
-				}
-				return result
-			}
-		}
-	}
-	return nil
-}
+// GetAudience returns the aud claim as a slice of strings (nil if absent).
+func (j *Token) GetAudience() []string {
+	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
+		return nil
+	}
+	aud, _ := j.processing.parsed.Claims.GetAudience()
+	if len(aud) == 0 {
+		return nil
+	}
+	return []string(aud)
+}

104-123: Normalize exp using jwt/v5 NumericDate (ms vs s tolerance stays intact).

Leverage Claims.GetExpirationTime() for correctness across claim encodings and avoid manual type switches. This also keeps your current behavior (returns whatever numeric units the token uses) but via the official helper.

-// GetExpiration returns the exp claim of the token.
-func (j *Token) GetExpiration() (int64, bool) {
-	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
-		return 0, false
-	}
-	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		if exp, exists := claims["exp"]; exists {
-			switch expVal := exp.(type) {
-			case float64:
-				return int64(expVal), true
-			case int64:
-				return expVal, true
-			case int:
-				return int64(expVal), true
-			}
-		}
-	}
-	return 0, false
-}
+// GetExpiration returns the exp claim (Unix timestamp) if present.
+func (j *Token) GetExpiration() (int64, bool) {
+	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
+		return 0, false
+	}
+	nd, _ := j.processing.parsed.Claims.GetExpirationTime()
+	if nd == nil {
+		return 0, false
+	}
+	return nd.Time.Unix(), true
+}

Note: The JWT spec defines NumericDate in seconds. Some IdPs emit ms; if you need auto-conversion, we can add a heuristic (e.g., treat values > 253402300799 as ms and divide by 1000). Happy to wire that if desired.


124-143: Use jwt/v5 helper for iat to reduce type branching.

-// GetIssuedAt returns the iat claim of the token.
-func (j *Token) GetIssuedAt() (int64, bool) {
-	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
-		return 0, false
-	}
-	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		if iat, exists := claims["iat"]; exists {
-			switch iatVal := iat.(type) {
-			case float64:
-				return int64(iatVal), true
-			case int64:
-				return iatVal, true
-			case int:
-				return int64(iatVal), true
-			}
-		}
-	}
-	return 0, false
-}
+// GetIssuedAt returns the iat claim (Unix timestamp) if present.
+func (j *Token) GetIssuedAt() (int64, bool) {
+	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
+		return 0, false
+	}
+	nd, _ := j.processing.parsed.Claims.GetIssuedAt()
+	if nd == nil {
+		return 0, false
+	}
+	return nd.Time.Unix(), true
+}

144-157: Use jwt/v5 helper for jti.

-// GetJWTID returns the jti claim of the token.
-func (j *Token) GetJWTID() string {
-	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
-		return ""
-	}
-	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		if jti, exists := claims["jti"]; exists {
-			if jtiStr, ok := jti.(string); ok {
-				return jtiStr
-			}
-		}
-	}
-	return ""
-}
+// GetJWTID returns the jti claim of the token.
+func (j *Token) GetJWTID() string {
+	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
+		return ""
+	}
+	jti, _ := j.processing.parsed.Claims.GetID()
+	return jti
+}
``]

---

`159-178`: **Broaden permissions parsing to handle common shapes.**

Many providers emit permissions as []string or a single space-/comma-delimited string. Current code only handles []interface{}.


```diff
-// GetPermissions returns the permissions claim of the token.
-func (j *Token) GetPermissions() []string {
+// GetPermissions returns the permissions claim of the token.
+func (j *Token) GetPermissions() []string {
 	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
 		return nil
 	}
-	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		if permissions, exists := claims["permissions"]; exists {
-			if perms, ok := permissions.([]interface{}); ok {
-				result := make([]string, 0, len(perms))
-				for _, p := range perms {
-					if pStr, ok := p.(string); ok {
-						result = append(result, pStr)
-					}
-				}
-				return result
-			}
-		}
-	}
+	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
+		if raw, exists := claims["permissions"]; exists {
+			switch v := raw.(type) {
+			case []interface{}:
+				out := make([]string, 0, len(v))
+				for _, p := range v {
+					if s, ok := p.(string); ok {
+						out = append(out, s)
+					}
+				}
+				if len(out) > 0 {
+					return out
+				}
+			case []string:
+				if len(v) > 0 {
+					return append([]string(nil), v...)
+				}
+			case string:
+				if v != "" {
+					// support both space and comma delimiters
+					sep := ","
+					if bytesIndex := func() int { for i := range v { if v[i] == ' ' { return i } }; return -1 }(); bytesIndex != -1 {
+						sep = " "
+					}
+					parts := strings.Split(v, sep)
+					out := make([]string, 0, len(parts))
+					for _, p := range parts {
+						if ps := strings.TrimSpace(p); ps != "" {
+							out = append(out, ps)
+						}
+					}
+					if len(out) > 0 {
+						return out
+					}
+				}
+			}
+		}
+	}
 	return nil
 }

Note: add imports if you adopt the string parsing:

import "strings"

180-199: Handle scp as both array and string.

scp often arrives as a space-delimited string. Extend parsing to cover both.

-// GetScopes returns the scp claim of the token.
-func (j *Token) GetScopes() []string {
+// GetScopes returns the scp claim of the token.
+func (j *Token) GetScopes() []string {
 	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
 		return nil
 	}
 	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		if scopes, exists := claims["scp"]; exists {
-			if scps, ok := scopes.([]interface{}); ok {
-				result := make([]string, 0, len(scps))
-				for _, s := range scps {
-					if sStr, ok := s.(string); ok {
-						result = append(result, sStr)
-					}
-				}
-				return result
-			}
-		}
+		if raw, exists := claims["scp"]; exists {
+			switch v := raw.(type) {
+			case []interface{}:
+				out := make([]string, 0, len(v))
+				for _, s := range v {
+					if ss, ok := s.(string); ok {
+						out = append(out, ss)
+					}
+				}
+				if len(out) > 0 {
+					return out
+				}
+			case []string:
+				if len(v) > 0 {
+					return append([]string(nil), v...)
+				}
+			case string:
+				if v != "" {
+					parts := strings.Fields(v) // split on whitespace
+					if len(parts) > 0 {
+						return parts
+					}
+				}
+			}
+		}
 	}
 	return nil
 }

Note: add import "strings" if not already added.


237-265: Consider returning an empty map instead of nil for feature flags.

Returning an empty map simplifies callers (no nil-checks before range). Tests currently expect nil for “missing,” so this is optional and would require test alignment.

If you decide to switch, update:

-		return nil
+		return map[string]FeatureFlag{}
...
-	return nil
+	return map[string]FeatureFlag{}

328-337: Avoid exposing internal claims map; return a shallow copy.

Returning the underlying MapClaims allows external mutation of parsed token state. Prefer returning a copy.

-// GetClaims returns the claims of the token.
-func (j *Token) GetClaims() map[string]any {
-	if j.processing.parsed == nil {
-		return make(map[string]any)
-	}
-	if claims, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
-		return claims
-	}
-	return make(map[string]any)
-}
+// GetClaims returns a shallow copy of the token claims.
+func (j *Token) GetClaims() map[string]any {
+	if j.processing.parsed == nil || j.processing.parsed.Claims == nil {
+		return map[string]any{}
+	}
+	if mc, ok := j.processing.parsed.Claims.(golangjwt.MapClaims); ok {
+		out := make(map[string]any, len(mc))
+		for k, v := range mc {
+			out[k] = v
+		}
+		return out
+	}
+	return map[string]any{}
+}
jwt/jwt_test.go (5)

299-335: Add coverage for permissions as string and []string.

Some providers encode permissions as "perm1 perm2" or []string. Extend tests to lock the desired behavior.

Add subtests:

t.Run("returns permissions when permissions claim is space-delimited string", func(t *testing.T) {
	token := &Token{
		processing: tokenProcessing{
			parsed: &golangjwt.Token{
				Claims: golangjwt.MapClaims{
					"permissions": "read:users read:competitions",
				},
			},
		},
	}
	perms := token.GetPermissions()
	assert.Equal(t, []string{"read:users", "read:competitions"}, perms)
})

t.Run("returns permissions when permissions claim is []string", func(t *testing.T) {
	token := &Token{
		processing: tokenProcessing{
			parsed: &golangjwt.Token{
				Claims: golangjwt.MapClaims{
					"permissions": []string{"read:users", "read:competitions"},
				},
			},
		},
	}
	perms := token.GetPermissions()
	assert.Equal(t, []string{"read:users", "read:competitions"}, perms)
})

337-373: Add coverage for scp as a space-delimited string.

scp frequently appears as a single string (“openid profile email”). Locking this in tests will prevent regressions.

Add a subtest:

t.Run("returns scopes when scp claim is space-delimited string", func(t *testing.T) {
	token := &Token{
		processing: tokenProcessing{
			parsed: &golangjwt.Token{
				Claims: golangjwt.MapClaims{
					"scp": "openid profile email offline",
				},
			},
		},
	}
	scopes := token.GetScopes()
	assert.Equal(t, []string{"openid", "profile", "email", "offline"}, scopes)
})

576-596: Clarify AsString expectations and add nil-token test.

Currently we only test the happy path. Add a test to document behavior when rawToken is nil and to note that WithExtra fields (e.g., id_token) aren’t included in JSON.

Add:

t.Run("returns 'null' when raw token is nil", func(t *testing.T) {
	token := &Token{}
	s, err := token.AsString()
	assert.NoError(t, err)
	assert.Equal(t, "null", s) // json.Marshal(nil) -> "null"
})

// Optional: assert extras are not present in JSON output
t.Run("extras are not serialized by AsString", func(t *testing.T) {
	raw := (&oauth2.Token{}).WithExtra(map[string]any{"id_token": "test_id"})
	token := &Token{rawToken: raw}
	s, err := token.AsString()
	assert.NoError(t, err)
	assert.NotContains(t, s, "test_id") // extras unexported; not in JSON
})

If you adopt an AsRedactedString helper, mirror tests to assert masking of AccessToken/RefreshToken.


598-614: Add tests for GetValidationErrors empty vs non-empty.

Guard against accidental non-nil error when no validation errors exist.

Add:

t.Run("GetValidationErrors returns nil when empty", func(t *testing.T) {
	token := &Token{validationErrors: nil}
	assert.Nil(t, token.GetValidationErrors())
})

t.Run("GetValidationErrors returns aggregated error when non-empty", func(t *testing.T) {
	errs := []error{assert.AnError}
	token := &Token{validationErrors: errs}
	err := token.GetValidationErrors()
	assert.Error(t, err)
})

74-110: Overall tests look solid; consider a small helper to reduce repetition.

There’s repeated construction of Token{processing: tokenProcessing{parsed: &jwt.Token{Claims: MapClaims{...}}}}. Introduce a small helper to create tokens from claims to tighten the tests.

func tokenWithClaims(mc golangjwt.MapClaims) *Token {
	return &Token{processing: tokenProcessing{parsed: &golangjwt.Token{Claims: mc}}}
}

Then replace repetitive blocks with:

token := tokenWithClaims(golangjwt.MapClaims{ "scp": []interface{}{"openid"} })

Also applies to: 112-163, 165-218, 220-259, 261-297, 375-449, 451-470, 472-505, 506-540, 541-575, 616-648, 650-758, 760-804, 805-877, 878-950, 951-1051, 1053-1070

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a74c64d and 83ac830.

📒 Files selected for processing (2)
  • jwt/jwt_test.go (2 hunks)
  • jwt/token.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
jwt/jwt_test.go (2)
jwt/jwt.go (1)
  • Token (22-27)
jwt/token.go (1)
  • FeatureFlag (232-235)
jwt/token.go (1)
jwt/jwt.go (1)
  • Token (22-27)
🔇 Additional comments (2)
jwt/token.go (2)

56-63: LGTM: Subject retrieval uses jwt/v5 helpers.

Using Claims.GetSubject() is the right move for both MapClaims and RegisteredClaims.


317-326: LGTM: Minimal, safe coercion helper.

toString is intentionally strict; appropriate for typing the feature-flag “t” field.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

18-18: Fix typos and a likely package name mistake in docs

These are user-facing docs; typos and a probable incorrect import/package reference can confuse readers.

Recommended fixes:

  • Line 18: “Autorization” → “Authorization”.
  • Lines 22–23: “slient secret” → “client secret”; “not exposes tokens” → “does not expose tokens”; consider “session cookie” → “a session cookie”.
  • Lines 54–55: Device flow is shown under the authorization_code package. If this is actually a distinct device authorization flow, the import likely should be github.com/kinde-oss/kinde-go/oauth2/device_authorization (or whatever the package is named). Please confirm and correct.
  • Line 56: “separatees” → “separates”; also “environment with the limited input capabilities” → “environments with limited input capabilities”.
  • Lines 61–65 (comments in snippet): “applicaiton” → “application”; “retreiving” → “retrieving”.
  • Line 71: “will provide following methods” → “provides the following methods”.
  • Lines 95–99: “optioanlly” → “optionally”; “aquired” → “acquired”.
  • Line 116: “willl” → “will”.

I can submit a follow-up PR with these corrections if you prefer.

Also applies to: 22-23, 54-56, 61-65, 71-71, 95-99, 116-116

🧹 Nitpick comments (3)
README.md (3)

165-171: Tighten phrasing, fix “JWT tokens” tautology, and align OAuth2 naming

Minor wording and style nits to improve clarity and consistency. Also expands examples of validation options and token info.

Apply this diff:

-**Quick Overview:**
-
-- Parse JWT tokens from HTTP headers, strings, session storage, or OAuth2 tokens
-- Flexible validation options (algorithm, audience, issuer, claims, etc.)
-- JWKS support for token signature validation
-- Comprehensive token information access
-- Seamless integration with OAuth2 flows
+**Quick Overview:**
+
+- Parse JWTs from HTTP headers, raw strings, session storage, or OAuth 2.0 token responses.
+- Flexible validation options (algorithm, audience, issuer, required claims, clock skew, custom time function).
+- JWKS‑based signature verification.
+- Access to comprehensive token information (subject, issuer, audience, expiry, claims, feature flags).
+- Seamless integration with OAuth 2.0 flows.

161-171: Optional: include a one‑line import hint to reduce friction

Even though details moved to jwt/README.md, adding a tiny import hint improves DX without bloating this page.

For example, after the Quick Overview:

import "github.com/kinde-oss/kinde-go/jwt"

163-163: Verify jwt/README.md link and API surface

  • ✅ Confirmed jwt/README.md exists and the relative link from README.md:163 resolves correctly on GitHub.
  • ✅ Documented parsing methods in jwt/README.md:
    ParseFromString (lines 46–54)
    ParseFromAuthorizationHeader (lines 57–64)
    ParseFromSessionStorage (lines 69–77)
  • ⚠️ No ParseOAuth2Token (or ParseFromOAuth2Token) helper is defined in the README. If that method exists in code, please add its documentation; otherwise, remove or correct this reference.
  • ✅ Verified getters and helpers are all present:
    IsValid, GetSubject, GetIssuer, GetAudience, GetClaims, AsString, GetValidationErrors (e.g. lines 36–40, 223–246)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 83ac830 and 9e2e664.

📒 Files selected for processing (2)
  • README.md (1 hunks)
  • jwt/README.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • jwt/README.md
🧰 Additional context used
🪛 LanguageTool
README.md

[grammar] ~168-~168: There might be a mistake here.
Context: ...gorithm, audience, issuer, claims, etc.) - JWKS support for token signature validat...

(QB_NEW_EN)


[grammar] ~169-~169: There might be a mistake here.
Context: ...S support for token signature validation - Comprehensive token information access -...

(QB_NEW_EN)


[grammar] ~170-~170: There might be a mistake here.
Context: ...- Comprehensive token information access - Seamless integration with OAuth2 flows ...

(QB_NEW_EN)

🔇 Additional comments (1)
README.md (1)

161-164: Good reorg: root README stays high-level and defers details to jwt/README.md

The new “JWT Package” section is concise and points to the dedicated docs. This reduces duplication and keeps the root README focused. Nice.

@evgenyk
evgenyk merged commit 39f4635 into main Aug 26, 2025
4 checks passed
@evgenyk
evgenyk deleted the ev/token_methods branch September 6, 2025 04:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant