| 208 | } |
| 209 | |
| 210 | async exchangeRefreshToken( |
| 211 | client: OAuthClientInformationFull, |
| 212 | refreshToken: string, |
| 213 | scopes?: string[], |
| 214 | resource?: URL, |
| 215 | ): Promise<OAuthTokens> { |
| 216 | const refreshTokenHash = hashToken(refreshToken); |
| 217 | const record = this.oauthStore.getRefreshToken(refreshTokenHash); |
| 218 | if (!record || record.clientId !== client.client_id || record.expiresAt < Math.floor(Date.now() / 1000)) { |
| 219 | throw new InvalidGrantError("Invalid refresh token"); |
| 220 | } |
| 221 | if (resource && !checkResourceAllowed({ requestedResource: resource, configuredResource: this.resourceServerUrl })) { |
| 222 | throw new InvalidGrantError("Invalid resource"); |
| 223 | } |
| 224 | |
| 225 | const requestedScopes = scopes ?? record.scopes; |
| 226 | if (!requestedScopes.every((scope) => record.scopes.includes(scope))) { |
| 227 | throw new AccessDeniedError("Refresh token cannot grant requested scopes"); |
| 228 | } |
| 229 | |
| 230 | return this.issueTokens( |
| 231 | client.client_id, |
| 232 | requestedScopes, |
| 233 | resource ?? (record.resource ? new URL(record.resource) : undefined), |
| 234 | refreshTokenHash, |
| 235 | ); |
| 236 | } |
| 237 | |
| 238 | async verifyAccessToken(token: string): Promise<AuthInfo> { |
| 239 | const record = this.oauthStore.getAccessToken(hashToken(token)); |