-
Notifications
You must be signed in to change notification settings - Fork 338
feat(backend,nextjs): Introduce machine authentication #5689
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
base: main
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
🦋 Changeset detectedLatest commit: 178e823 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great so far!
}); | ||
|
||
it('returns false for tokens without a recognized prefix', () => { | ||
expect(isMachineToken('unknown_prefix_token')).toBe(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wanna note that we do plan to allow custom prefixes in the future - likely these end up being prepended to the token type prefix so i think it should be a fairly straightforward change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a note to cover this in the future.
}); | ||
|
||
// Test each token type with parameterized tests | ||
const tokenTypes = ['api_key', 'oauth_token', 'machine_token'] as const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm mildly confused by the typecasting here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the as const
is needed here so TS knows these are literal types that match the keys in our mock objects, otherwise it would just see it as string[]
const { sessionTokenInHeader } = authenticateContext; | ||
if (!sessionTokenInHeader) { | ||
return handleError(new Error('No token in header'), 'header'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something seems weird about this logic...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe in practice this shouldn't be hit, as we check the existence of the header token before calling this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah either that or we remove and do non-null assertions
const authenticateContext = await createAuthenticateContext(createClerkRequest(request), options); | ||
assertValidSecretKey(authenticateContext.secretKey); | ||
|
||
// Default tokenType is session_token for backwards compatibility. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So do we want/need to add a TODO to change this behavior in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there are plans to change the behavior in the future yet. But I will update as soon as I get more info!
* The type of token to accept. | ||
* @default 'session_token' | ||
*/ | ||
acceptsToken?: TokenType | TokenType[] | 'any'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer APIs where there is no mix between string and array of strings.
So default would be ['session_token']
. Then I don't need to wrap it with an array when I quickly want to switch to two token types.
.changeset/chatty-lions-stay.md
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a minor update for these packages since we changed the auth
type from AuthObject
to SignedInAuthObject | SignedOutAuthObject
for backwards compat
.changeset/bumpy-carpets-study.md
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though this is a major update, the only public API that is breaking is AuthObject
. Previously, it's a union of SignedInAuthObject | SignedOutAuthObject
but now it's
export type AuthObject =
| SignedInAuthObject
| SignedOutAuthObject
| AuthenticatedMachineObject
| UnauthenticatedMachineObject;
Description
This PR adds machine authentication support (atm only in the backend SDK) by introducing support for 4 token types:
api_key
,oauth_token
,machine_token
, andsession_token
. To maintain backwards compatibility,session_token
remains the default authentication method when no specific token type is specified. This ensures existing apps continue to work without modification while allowing new applications to opt-in to machine authentication methods through theacceptsToken
option.Key changes:
SignedInState
andSignedOutState
in favor ofAuthenticatedState
andUnauthenticatedState
to better represent both session and machine authentication states. They still return the same properties, with an addedtokenType
andisAuthenticated
properties (deprecatingisSignedIn
).toAuth()
method now returns a different value if thetokenType
is not asession_token
. For now, we landed on theid
,name
,subject
,claims
andscopes
property for machine auth tokens.authenticateRequest
:authenticateAnyRequestWithTokenInHeader
andauthenticateMachineRequestWithTokenInHeader
to handle machine authentication.signedIn
andsignedOut
functions have been updated to accommodate machine auth.MachineTokenVerificationErrorCode
)APIKeysApi
,IdPOAuthAccessTokenApi
, andMachineTokensApi
) used inside a newverifyMachineAuthToken
function to validate tokens against their respective endpointsHere's an example usage pattern with API key:
Say C1 wants to protect their endpoints in a Hono app:
Then C2 can access it by passing the api_key:
P.S. I attempted to break this down into smaller PRs but the changes are tightly coupled 😞. So sorry and thank you in advance reviewer! I believe 30-40% of the total changes are from the test files.
Resolves ROBO-36
Checklist
pnpm test
runs as expected.pnpm build
runs as expected.Type of change