Skip to content

Commit

Permalink
Merge pull request #16 from saitamau-maximum/jwt
Browse files Browse the repository at this point in the history
JWT
  • Loading branch information
a01sa01to authored Sep 28, 2024
2 parents 60e8a38 + 234feba commit c220eb2
Show file tree
Hide file tree
Showing 32 changed files with 1,012 additions and 1,679 deletions.
12 changes: 1 addition & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,6 @@ if (!validation) {

JS/TS 以外の言語を使いたい場合、 `package/src/validate.ts` を参考にしてください。

### ログイン状態の確認

```javascript
import { checkLoggedIn } from '@saitamau-maximum/auth'

await checkLoggedIn(request, publicKey) // => true/false
```

ユーザー情報の取得と合わせてログイン状態の確認を行うことも可能 (↓)

### ユーザー情報の取得

```javascript
Expand All @@ -141,7 +131,7 @@ const userinfo = await getUserInfo(request, options)
```

クライアントサイドで取得する場合、 `/auth/me` にリクエストを送ってください。
秘密鍵など漏洩の可能性があるため、 `getUserInfo` は使わないようにしてください。
秘密鍵など漏洩の可能性があるため、クライアントサイドで `getUserInfo` は使わないようにしてください。

### ログイン

Expand Down
2 changes: 1 addition & 1 deletion package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@
},
"dependencies": {
"cookie": "^0.6.0",
"dayjs": "^1.11.13"
"jose": "^5.9.3"
}
}
2 changes: 1 addition & 1 deletion package/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { UserInfo, checkLoggedIn, getUserInfo } from './userinfo'
export { UserInfo, getUserInfo } from './userinfo'
export { middleware } from './middleware'
export { validateRequest } from './validate'
48 changes: 0 additions & 48 deletions package/src/internal/goparam.ts

This file was deleted.

72 changes: 30 additions & 42 deletions package/src/internal/handleCallback.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import { parse as parseCookie, serialize as serializeCookie } from 'cookie'
import { jwtVerify, SignJWT } from 'jose'

import { UserInfo } from '../userinfo'

import { AUTH_PUBKEY } from './const'
import { cookieOptions } from './cookie'
import { importKey, sign, verify } from './keygen'
import { importKey, keypairProtectedHeader } from './keygen'

interface Options {
/**
Expand Down Expand Up @@ -188,11 +191,7 @@ export const handleCallback = async (
})
}

if (
['authdata', 'iv', 'signature', 'signatureIv'].some(
key => !param.has(key) || param.getAll(key).length !== 1,
)
) {
if (!param.has('token')) {
return new Response('invalid request', { status: 400 })
}

Expand All @@ -205,19 +204,32 @@ export const handleCallback = async (
options.authPubkey || AUTH_PUBKEY,
'publicKey',
)
const privateKey = await importKey(options.privateKey, 'privateKey')

if (
!(await verify(
param.get('authdata') as string,
param.get('signature') as string,
authPubkey,
)) ||
!(await verify(param.get('iv')!, param.get('signatureIv')!, authPubkey))
) {
return new Response('invalid signature', { status: 400 })
const { payload } = await jwtVerify<UserInfo>(
param.get('token')!,
authPubkey,
{
algorithms: [keypairProtectedHeader.alg],
audience: options.authName,
clockTolerance: 5,
issuer: 'maximum-auth',
subject: 'Maximum Auth Data',
},
).catch(() => ({ payload: null }))
if (!payload) {
return new Response('invalid token', { status: 400 })
}

const privateKey = await importKey(options.privateKey, 'privateKey')
const newJwt = await new SignJWT(payload)
.setSubject('Maximum Auth Data')
.setAudience(options.authName)
.setIssuer(options.authName)
.setNotBefore('0 sec')
.setIssuedAt()
.setExpirationTime('1 day')
.setProtectedHeader(keypairProtectedHeader)
.sign(privateKey)

const continueUrl = parseCookie(cookieData)['__continue_to']

const newHeader = new Headers(request.headers)
Expand All @@ -230,31 +242,7 @@ export const handleCallback = async (
)
newHeader.append(
'Set-Cookie',
serializeCookie('__authdata', param.get('authdata')!, cookieOptions),
)
newHeader.append(
'Set-Cookie',
serializeCookie('__iv', param.get('iv')!, cookieOptions),
)
newHeader.append(
'Set-Cookie',
serializeCookie('__sign1', param.get('signature')!, cookieOptions),
)
newHeader.append(
'Set-Cookie',
serializeCookie(
'__sign2',
await sign(param.get('authdata')!, privateKey),
cookieOptions,
),
)
newHeader.append(
'Set-Cookie',
serializeCookie(
'__sign3',
await sign(param.get('iv')!, privateKey),
cookieOptions,
),
serializeCookie('token', newJwt, cookieOptions),
)
newHeader.set('Location', continueUrl || '/')

Expand Down
31 changes: 14 additions & 17 deletions package/src/internal/handleLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { serialize as serializeCookie } from 'cookie'

import { AUTH_DOMAIN } from './const'
import { cookieOptions } from './cookie'
import { generateGoParam } from './goparam'
import { derivePublicKey, exportKey, importKey } from './keygen'

const usedCharacters = Array.from(
Expand Down Expand Up @@ -170,7 +169,7 @@ export const handleLogin = async (
const privkey = await importKey(options.privateKey, 'privateKey')
const pubkey = await derivePublicKey(privkey)

const redirectData = await fetch(`${authOrigin}/token`, {
const token = await fetch(`${authOrigin}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand All @@ -180,23 +179,21 @@ export const handleLogin = async (
pubkey: await exportKey(pubkey),
callback: callbackUrl,
}),
})
.then(res => res.json<{ token: string; iv: string }>())
.catch(() => {
throw new Error('auth server error', {
cause:
'authName or privateKey is incorrect. The auth server might be down.',
})
}).then(res => {
if (res.ok) {
return res.text()
}
throw new Error('auth server error', {
cause:
'authName or privateKey is incorrect. The auth server might be down.',
})
})

const param = await generateGoParam(
options.authName,
await exportKey(pubkey),
callbackUrl,
atob(redirectData.token),
atob(redirectData.iv),
privkey,
)
const param = new URLSearchParams()
param.set('name', options.authName)
param.set('pubkey', await exportKey(pubkey))
param.set('callback', callbackUrl)
param.set('token', token)

return new Response(null, {
status: 302,
Expand Down
30 changes: 1 addition & 29 deletions package/src/internal/handleLogout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,35 +103,7 @@ export const handleLogout = async (request: Request): Promise<Response> => {
const newHeader = new Headers(request.headers)
newHeader.append(
'Set-Cookie',
serializeCookie('__authdata', '', {
...cookieOptions,
maxAge: -1,
}),
)
newHeader.append(
'Set-Cookie',
serializeCookie('__iv', '', {
...cookieOptions,
maxAge: -1,
}),
)
newHeader.append(
'Set-Cookie',
serializeCookie('__sign1', '', {
...cookieOptions,
maxAge: -1,
}),
)
newHeader.append(
'Set-Cookie',
serializeCookie('__sign2', '', {
...cookieOptions,
maxAge: -1,
}),
)
newHeader.append(
'Set-Cookie',
serializeCookie('__sign3', '', {
serializeCookie('token', '', {
...cookieOptions,
maxAge: -1,
}),
Expand Down
8 changes: 2 additions & 6 deletions package/src/internal/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
export { AUTH_DOMAIN, AUTH_PUBKEY, PROXY_PUBKEY } from './const'
export {
decrypt,
derivePublicKey,
encrypt,
exportKey,
generateKeyPair,
generateSymmetricKey,
importKey,
keypairGenAlgorithm,
keypairHashAlgorithm,
keypairProtectedHeader,
keypairUsage,
sign,
symmetricGenAlgorithm,
symmetricProtectedHeader,
symmetricUsage,
verify,
} from './keygen'
export { generateGoParam, verifyMac } from './goparam'
export { generateToken, verifyToken } from './tokengen'
export { handleCallback } from './handleCallback'
export { handleLogin } from './handleLogin'
Expand Down
Loading

0 comments on commit c220eb2

Please sign in to comment.