diff --git a/src/api/types.ts b/src/api/types.ts index bf61c90..1966e21 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,4 +1,6 @@ -export type Authentication = { __TYPE__: "Authentication" } +export type Authentication = { + principal: string +} export type Config = { __TYPE__: "Config" } diff --git a/src/aws/auth/ClientCredentialsAuthProvider.tsx b/src/aws/auth/ClientCredentialsAuthProvider.tsx index 156ed3a..5156fa7 100644 --- a/src/aws/auth/ClientCredentialsAuthProvider.tsx +++ b/src/aws/auth/ClientCredentialsAuthProvider.tsx @@ -23,9 +23,8 @@ export function ClientCredentialsAuthProvider({children}:ClientCredentialsAuthPr throw new Error("Invalid configuration. Required properties not found") } - const authenticationProvider = authenticateClientIdClientSecret(config.clientIdSecret) - const authentication = await authenticationProvider().then(validateCredentials) - setCredentials(authentication) + const credentials = await authenticateClientIdClientSecret(config.clientIdSecret)() + setCredentials(toAuthentication(config.clientIdSecret.clientId, credentials)) setLoading(false) } @@ -47,8 +46,11 @@ type ClientIdSecretProperties = { clientSecret: string } -function validateCredentials(credentials:Credentials):AWSAuthentication { - return credentials as AWSAuthentication +function toAuthentication(clientId:string, credentials:Credentials):AWSAuthentication { + return { + principal: clientId, + ...credentials + } } function isClientIdSecretConfig(config:Config):config is ClientIdSecretConfig { diff --git a/src/aws/auth/IdentityPoolAuthProvider.tsx b/src/aws/auth/IdentityPoolAuthProvider.tsx index 364f2f4..4ccc6d1 100644 --- a/src/aws/auth/IdentityPoolAuthProvider.tsx +++ b/src/aws/auth/IdentityPoolAuthProvider.tsx @@ -4,6 +4,7 @@ import { fromCognitoIdentityPool } from "@aws-sdk/credential-providers" import { createOIDCClient, ErrorResult, IdTokenResult, IssuerConfig } from "../../oauth2"; import { ConfigContext, AuthContext, Config } from "../../api" import { AWSAuthentication } from ".."; +import { IdToken } from '../../oauth2/common'; type IdentityPoolAuthProviderProps = { children: React.ReactNode @@ -84,8 +85,8 @@ export function IdentityPoolAuthProvider({children}:IdentityPoolAuthProviderProp region: config.cognito.region } }) - const authentication = await awsCredentialsProvider().then(validateCredentials) - setCredentials(authentication) + const congnitoIdentityCredentials = await awsCredentialsProvider() + setCredentials(toAuthentication(result.idToken, congnitoIdentityCredentials)) setLoading(false) } } @@ -99,6 +100,9 @@ export function IdentityPoolAuthProvider({children}:IdentityPoolAuthProviderProp } -function validateCredentials(credentials:Credentials):AWSAuthentication { - return credentials as AWSAuthentication +function toAuthentication(token:IdToken, credentials:Credentials):AWSAuthentication { + return { + principal: token.sub || "", + ...credentials + } } diff --git a/src/aws/realms/AWSBackend.ts b/src/aws/realms/AWSBackend.ts index 6960cab..e9a71d2 100644 --- a/src/aws/realms/AWSBackend.ts +++ b/src/aws/realms/AWSBackend.ts @@ -1,7 +1,7 @@ import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3" import { hostHeaderMiddlewareOptions } from "@aws-sdk/middleware-host-header" import { HttpRequest } from "@aws-sdk/protocol-http"; -import { Credentials, Provider } from "@aws-sdk/types" +import { Credentials } from "@aws-sdk/types" import { S3RealmsProperties } from './realms.api' export default class AWSBackend { @@ -44,7 +44,8 @@ export default class AWSBackend { return toString(Body as ReadableStream | Blob) } catch(error) { const { httpStatusCode } = error.$metadata - if(404 == httpStatusCode) { + // accomodate situations when user don't have ListBucket permissions + if(404 == httpStatusCode || 403 == httpStatusCode) { return '' } else { throw error diff --git a/src/aws/realms/realms.api.ts b/src/aws/realms/realms.api.ts index 8b7e97e..f9ebda7 100644 --- a/src/aws/realms/realms.api.ts +++ b/src/aws/realms/realms.api.ts @@ -1,10 +1,11 @@ import Papa from "papaparse" import AWSBackend from "./AWSBackend" -import { Credentials, Provider } from "@aws-sdk/types"; import { RealmDefinition } from "../../api" +import { AWSAuthentication } from ".."; let resolveBackend:(backend:AWSBackend) => void let resolveSource:(source:S3RealmsProperties) => void +let resolvePrincipal:(prinsipal:string) => void const awsBackendPromise = new Promise(resolve => { resolveBackend = resolve @@ -14,22 +15,30 @@ const awsSourcePromise = new Promise(resolve => { resolveSource = resolve }) +const principalPromise = new Promise(resolve => { + resolvePrincipal = resolve +}) + export type S3RealmsProperties = { bucket: string - object: string + // @deprecated + object?: string + objectPrefix?: string + objectName?: string endpoint?: string region?:string } -export const setupRealms = (source:S3RealmsProperties, credentials:Credentials) => { +export const setupRealms = (source:S3RealmsProperties, credentials:AWSAuthentication) => { resolveBackend(new AWSBackend(source, credentials)) resolveSource(source) + resolvePrincipal(credentials.principal) } export const fetchRealms = async () => { const awsBackend = await awsBackendPromise - const source = await awsSourcePromise + const source = await calculateObjectDetails() const value = await awsBackend.fetchResource(source.bucket, source.object) const realms = Papa.parse(value, { header: true, transform: transform }).data realms?.forEach(realm => realm.persisted = true) @@ -38,7 +47,7 @@ export const fetchRealms = async () => { export const pushRealms = async (realms:RealmDefinition[]) => { const awsBackend = await awsBackendPromise - const awsSource = await awsSourcePromise + const awsSource = await calculateObjectDetails() await awsBackend.storeResource(awsSource.bucket, awsSource.object, Papa.unparse(realms)) realms?.forEach(realm => realm.persisted = true) return realms @@ -50,3 +59,11 @@ function transform(value:string, headerName:string) { } return value; } + +async function calculateObjectDetails():Promise<{bucket:string, object: string}> { + const awsSource = await awsSourcePromise + const principal = await principalPromise + const bucket = awsSource.bucket + const path = principal + "/" + awsSource.object + return {bucket: bucket, object: path} +}