New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VerifyToken and getJWK without middleware in v2 #277
Comments
Hi @sonatard, happy to assist but I'm not sure I fully understand what you're trying to achieve. Sorry for that! The In order to verify the token's validity, it fetches the JSON Web key using the Clerk Backend API. If you already have the JSON Web Key, you can pass it as an option when using the middleware. WithHeaderAuthorization(JSONWebKey(theKey)) If you want to control the JSON web key fetching with a configurable client, you can pass a jwks.Client as an option WithHeaderAuthorization(JWKSClient(theClient)) Both options above are also available as jwt.VerifyParams if you need to call In order to retrieve the JSON Web Key that you need, you need first retrieve the JSON Web Key Set for your instance and then filter the results to get the key you need. You can use the JWKS Clerk Backend API operation with the jwks package to fetch the JSON Web Key Set. If you don't mind me asking, what's your use-case for needing to re-implement the |
The reason I want to create my own middleware is because I want to perform different authentication based on headers within a single middleware. func (a *AuthenticationMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
switch r.Header.Get(httputil.HeaderClient) {
case httputil.HeaderClientAdmin:
// Clerk Auth
case httputil.HeaderClientOther:
// Other auth
default:
panic("unreachable")
}
} |
The current jwks package does not expose caching functionality. I need to implement my own caching mechanism. That means I have to create my own getJWK function. Or if a function like this was exposed, I could implement the middleware myself. func Verify(ctx context.Context, token string, opts ...clerkhttp.AuthorizationOption) (*clerk.SessionClaims, error) {
decoded, err := jwt.Decode(ctx, &jwt.DecodeParams{Token: token})
if err != nil {
return nil, err
}
params := &clerkhttp.AuthorizationParams{}
for _, opt := range opts {
err := opt(params)
if err != nil {
return nil, err
}
}
if params.Clock == nil {
params.Clock = clerk.NewClock()
}
if params.JWK == nil {
params.JWK, err = getJWK(ctx, params.JWKSClient, decoded.KeyID, params.Clock)
if err != nil {
return nil, err
}
}
params.Token = token
claims, err := jwt.Verify(ctx, ¶ms.VerifyParams)
if err != nil {
return nil, err
}
return claims, nil
} func WithHeaderAuthorization(opts ...clerkhttp.AuthorizationOption) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
switch r.Header.Get(httputil.HeaderClient) {
case httputil.HeaderClientAdmin:
authorization := strings.TrimSpace(r.Header.Get("Authorization"))
if authorization == "" {
next.ServeHTTP(w, r)
return
}
token := strings.TrimPrefix(authorization, "Bearer ")
claims, err := verify(ctx, token, opts...)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
newCtx := clerk.ContextWithSessionClaims(ctx, claims)
next.ServeHTTP(w, r.WithContext(newCtx))
case httputil.HeaderClientOther:
// Other auth
}
})
}
} I would be happy if functions focused more on pure functionality were provided rather than functions tied to middleware. |
Hi @sonatard thanks for the detailed explanation! It helps to see what the use case is and how we can better support it.
This was an intentional design choice. No other endpoint supports caching. Users are free to add a caching layer of their own if they choose to.
I guess the problem is that the With the current state of things would something like the following solve your problem? WithHeaderAuthorization(WithCustomAuthorization(handler))
func WithCustomAuthorization() {
switch r.Header.Get(httputil.HeaderClient) {
case httputil.HeaderClientOther:
// handle this case first
// ...
// add other known cases here
// ...
default:
// Let the next middleware take over.
// Next middleware will be the Clerk middleware which will check the
// httputil.HeaderClientAdmin case
next.ServeHTTP(w, r)
}
} If I'm not mistaken, it looks like you could also call the Clerk middleware from inside your custom auth middleware depending on the case, once you read the Alternatively, we could potentially add an option to bypass the default middleware behavior which responds with 401 Unauthorized and let the consumer declare the function to run upon failed authentication. WithHeaderAuthorization(OnFailure(customFailureFn))(handler)
func customFailureFn() {
// you can declare custom functionality that's
// going to be executed whenever Clerk authentication
// fails.
} Would that work? |
Yes, I think it is possible to implement it. However, I don't want the middleware layers to increase. |
I am currently working on migrating to v2.
But we cannot use this middleware directly as we want to implement other things inside the Middleware.
clerk-sdk-go/http/middleware.go
Lines 38 to 84 in 39301db
However, we want to implement jwt.VerifyToken and getJWK. Since getJWK is not public, we cannot use it as is. We can achieve the same thing by copying the implementation of getJWK, but how does the Clerk team recommend implementing it in such cases?
The text was updated successfully, but these errors were encountered: