Skip to content

Commit

Permalink
Merge pull request #47 from pilcrowOnPaper/amazon-cognito-provider
Browse files Browse the repository at this point in the history
Add Amazon Cognito provider
  • Loading branch information
pilcrowonpaper authored Jan 27, 2024
2 parents b45d753 + 39471ee commit 2c30d04
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const tokens = await github.validateAuthorizationCode(code);
## Supported providers

- Amazon Cognito
- Apple
- Atlassian
- Auth0
Expand Down
1 change: 1 addition & 0 deletions docs/malta.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
{
"title": "Providers",
"pages": [
["Amazon Cognito", "/providers/amazon-cognito"],
["Apple", "/providers/apple"],
["Atlassian", "/providers/atlassian"],
["Auth0", "/providers/auth0"],
Expand Down
42 changes: 42 additions & 0 deletions docs/pages/providers/amazon-cognito.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: "Amazon Cognito"
---

# Amazon Cognito

Implements OpenID Connect.

For usage, see [OAuth 2.0 provider with PKCE](/guides/oauth2-pkce).

```ts
import { AmazonCognito } from "arctic";

const userPoolDomain = "https://example.auth.region.amazoncognito.com";
const amazonCognito = new AmazonCognito(userPoolDomain, clientId, clientSecret, redirectURI);
```

```ts
const url: URL = await amazonCognito.createAuthorizationURL(state, codeVerifier, {
// optional
scopes // "openid" always included
});
const tokens: AmazonCognitoTokens = await amazonCognito.validateAuthorizationCode(
code,
codeVerifier
);
const tokens: AmazonCognitoRefreshedTokens = await amazonCognito.refreshAccessToken(refreshToken);
```

## Get user profile

Parse the ID token or use the `userinfo` endpoint. See [sample ID token claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims).

```ts
const tokens = await amazonCognito.validateAuthorizationCode(code, codeVerifier);
const response = await fetch(userPoolDomain + "/oauth/userInfo", {
headers: {
Authorization: `Bearer ${tokens.accessToken}`
}
});
const user = await response.json();
```
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { AmazonCognito } from "./providers/amazon-cognito.js";
export { Apple } from "./providers/apple.js";
export { Atlassian } from "./providers/atlassian.js";
export { Auth0 } from "./providers/auth0.js";
Expand Down Expand Up @@ -27,6 +28,10 @@ export { Twitch } from "./providers/twitch.js";
export { Twitter } from "./providers/twitter.js";
export { Yahoo } from "./providers/yahoo.js";

export type {
AmazonCognitoRefreshedTokens,
AmazonCognitoTokens
} from "./providers/amazon-cognito.js";
export type { AppleCredentials, AppleRefreshedTokens, AppleTokens } from "./providers/apple.js";
export type { AtlassianTokens } from "./providers/atlassian.js";
export type { Auth0Tokens } from "./providers/auth0.js";
Expand Down
91 changes: 91 additions & 0 deletions src/providers/amazon-cognito.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { TimeSpan, createDate } from "oslo";
import { OAuth2Client } from "oslo/oauth2";

import type { OAuth2ProviderWithPKCE } from "../index.js";

export class AmazonCognito implements OAuth2ProviderWithPKCE {
private client: OAuth2Client;
private clientSecret: string;

constructor(userPoolDomain: string, clientId: string, clientSecret: string, redirectURI: string) {
const authorizeEndpoint = userPoolDomain + "/oauth2/authorize";
const tokenEndpoint = userPoolDomain + "/oauth2/token";
this.client = new OAuth2Client(clientId, authorizeEndpoint, tokenEndpoint, {
redirectURI
});
this.clientSecret = clientSecret;
}

public async createAuthorizationURL(
state: string,
codeVerifier: string,
options?: {
scopes?: string[];
}
): Promise<URL> {
const scopes = options?.scopes ?? [];
return await this.client.createAuthorizationURL({
state,
codeVerifier,
scopes: [...scopes, "openid"]
});
}

public async validateAuthorizationCode(
code: string,
codeVerifier: string
): Promise<AmazonCognitoTokens> {
const result = await this.client.validateAuthorizationCode<AuthorizationCodeResponseBody>(
code,
{
credentials: this.clientSecret,
codeVerifier
}
);
const tokens: AmazonCognitoTokens = {
accessToken: result.access_token,
refreshToken: result.refresh_token,
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")),
idToken: result.id_token
};
return tokens;
}

public async refreshAccessToken(refreshToken: string): Promise<AmazonCognitoRefreshedTokens> {
const result = await this.client.refreshAccessToken<RefreshTokenResponseBody>(refreshToken, {
credentials: this.clientSecret
});
const tokens: AmazonCognitoRefreshedTokens = {
accessToken: result.access_token,
accessTokenExpiresAt: createDate(new TimeSpan(result.expires_in, "s")),
idToken: result.id_token
};
return tokens;
}
}

interface AuthorizationCodeResponseBody {
access_token: string;
refresh_token: string;
expires_in: number;
id_token: string;
}

interface RefreshTokenResponseBody {
access_token: string;
expires_in: number;
id_token: string;
}

export interface AmazonCognitoTokens {
accessToken: string;
refreshToken: string;
accessTokenExpiresAt: Date;
idToken: string;
}

export interface AmazonCognitoRefreshedTokens {
accessToken: string;
accessTokenExpiresAt: Date;
idToken: string;
}

0 comments on commit 2c30d04

Please sign in to comment.