> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fastfoundation.nimble.la/llms.txt
> Use this file to discover all available pages before exploring further.

# Use SSO Profiles in Applications

> Integrate AWS Single Sign-On (SSO) profiles into your applications and development workflow

# Integrate SSO into Your Code

Use **AWS Single Sign-On (SSO)** profiles locally and **Identity and Access Management (IAM)** roles in the cloud for secure, environment-aware access.

## Development Principles

* **Local vs. cloud**: Use **SSO profiles** for local development; use **IAM roles** in cloud environments
* **Least privilege**: Grant only the permissions your application needs
* **No hardcoded secrets**: Never store access keys or sensitive data in code or `.env` files
* **Consistent naming**: Reuse the same profile name across projects (e.g., `<project>-workload-development`)

## Credential Resolution Overview

Your app decides credentials depending on the **runtime environment**:

```mermaid theme={null}
graph TD
    A[Application Start] --> B{Environment Check}
    B -->|Local Development| C[Use SSO Profile]
    B -->|Cloud Environment| D[Use IAM Roles]
    C --> E[Configure SDK with Profile]
    D --> F[Use Instance/Container Role]
    E --> G[AWS Client Ready]
    F --> G
```

### Environment Detection Pattern

Use a simple, reliable signal like AWS\_PROFILE, .env.local, or NODE\_ENV=development.

```bash theme={null}
import os, pathlib

def is_local():
    return bool(os.getenv("AWS_PROFILE")) \
        or os.getenv("NODE_ENV") == "development" \
        or pathlib.Path(".env.local").exists()
```

## Python Example (boto3)

### Installation

```bash theme={null}
# Install required packages
pip install boto3
```

### Basic Implementation

<Tabs>
  <Tab title="Environment Variable Method">
    ```python theme={null}
    def create_aws_client(service_name='s3'):
        """
        - Local: uses SSO profile if AWS_PROFILE is set
        - Cloud: falls back to default chain (instance/role)
        """
        profile = os.environ.get("AWS_PROFILE")
        region = os.environ.get("AWS_REGION", "us-west-2")

        if profile:
            try:
                boto3.setup_default_session(profile_name=profile, region_name=region)
            except ProfileNotFound as e:
                raise RuntimeError(f"AWS profile '{profile}' not found") from e
        else:
            boto3.setup_default_session(region_name=region)

        return boto3.client(service_name)

    if __name__ == "__main__":
        s3 = create_aws_client('s3')
        resp = s3.list_buckets()
        print([b['Name'] for b in resp.get('Buckets', [])])
    ```
  </Tab>

  <Tab title="Direct Profile Method">
    ```python theme={null}
    import boto3
    from botocore.exceptions import ProfileNotFound, NoCredentialsError

    def create_aws_client_with_profile(service_name='s3', profile_name=None, region='us-west-2'):
        try:
            if profile_name:
                session = boto3.Session(profile_name=profile_name, region_name=region)
                return session.client(service_name)
            # Cloud: default chain (role/metadata)
            return boto3.client(service_name, region_name=region)
        except ProfileNotFound as e:
            raise RuntimeError(f"AWS profile '{profile_name}' not found") from e
        except NoCredentialsError as e:
            raise RuntimeError("No AWS credentials available") from e

    # Usage example
    def example_usage():
        # For local development, set your SSO profile
        profile = "<project>-workload-development"

        # Create clients with the chosen profile
        s3_client = create_aws_client_with_profile('s3', profile)
        ses_client = create_aws_client_with_profile('ses', profile)

        # Example: list S3 buckets
        try:
            buckets = s3_client.list_buckets()
            print("S3 Buckets:")
            for b in buckets.get("Buckets", []):
                print(f"  - {b['Name']}")
        except Exception as e:
            print(f"Error listing buckets: {e}")

    if __name__ == "__main__":
        example_usage()
    ```
  </Tab>

  <Tab title="Class-Based Approach">
    ```python theme={null}
    import os
    import boto3
    from typing import Optional

    class AWSClientManager:
        """Centralized AWS client/session management with SSO support."""
        def __init__(self, profile_name: Optional[str] = None, region: Optional[str] = None):
            self.profile_name = profile_name or os.getenv("AWS_PROFILE")
            self.region = region or os.getenv("AWS_REGION", "us-west-2")
            self._session = None

        @property
        def session(self) -> boto3.session.Session:
            if not self._session:
                if self.profile_name:
                    self._session = boto3.Session(profile_name=self.profile_name, region_name=self.region)
                else:
                    self._session = boto3.Session(region_name=self.region)
            return self._session

        def client(self, service_name: str):
            return self.session.client(service_name)

        def resource(self, service_name: str):
            return self.session.resource(service_name)

    # Example
    if __name__ == "__main__":
        aws = AWSClientManager()
        s3 = aws.client('s3')
        print([b['Name'] for b in s3.list_buckets().get('Buckets', [])])
    ```
  </Tab>
</Tabs>

### Environment Configuration

```bash theme={null}
# Set profile for your development session
export AWS_PROFILE=<project>-<development_workload_account>
export AWS_REGION=us-west-2

# Run your Python application
python app.py
```

## Node.js Example (AWS SDK v3)

### Installation

```bash theme={null}
# Install required packages
npm install @aws-sdk/client-s3 @aws-sdk/credential-providers
```

### Implementation Examples

<Tabs>
  <Tab title="Environment Variable Method">
    ```javascript theme={null}
    import { S3Client, ListBucketsCommand } from '@aws-sdk/client-s3';
    import { fromSSO } from '@aws-sdk/credential-providers';

    /**
     * Create AWS client with environment-based credentials
     */
    function createS3() {
    const region = process.env.AWS_REGION || 'us-west-2';
    const profile = process.env.AWS_PROFILE;

    if (profile) {
        // Local with SSO
        return new S3Client({
        region,
        credentials: fromSSO({ profile })
        });
    }
    // Cloud: default provider chain (role)
    return new S3Client({ region });
    }

    export async function listS3Buckets() {
    const s3 = createS3();
    const out = await s3.send(new ListBucketsCommand({}));
    console.log('S3 Buckets:', out.Buckets?.map(b => b.Name));
    return out.Buckets;
    }

    // Run example
    listS3Buckets().catch(console.error);
    ```
  </Tab>

  <Tab title="Direct Profile Method">
    ```javascript theme={null}
    import { S3Client } from '@aws-sdk/client-s3';
    import { SESClient } from '@aws-sdk/client-ses';
    import { fromSSO } from '@aws-sdk/credential-providers';

    class AWSClientFactory {
    constructor(profileName = process.env.AWS_PROFILE, region = process.env.AWS_REGION || 'us-west-2') {
        this.profileName = profileName;
        this.region = region;
    }

    config() {
        const cfg = { region: this.region };
        if (this.profileName) cfg.credentials = fromSSO({ profile: this.profileName });
        return cfg;
    }

    s3()  { return new S3Client(this.config()); }
    ses() { return new SESClient(this.config()); }
    }

    async function example() {
    const factory = new AWSClientFactory('<project>-<production_workload_account>');
    const s3 = factory.s3();
    const buckets = await s3.send(new ListBucketsCommand({}));
    console.log(`Found ${buckets.Buckets?.length || 0} buckets`);
    }

    example().catch(console.error);
    ```
  </Tab>

  <Tab title="Configuration Manager">
    ```javascript theme={null}
    import { S3Client } from '@aws-sdk/client-s3';
    import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
    import { fromSSO } from '@aws-sdk/credential-providers';

    class AWSConfig {
        constructor() {
            this.profile = process.env.AWS_PROFILE;
            this.region  = process.env.AWS_REGION || 'us-west-2';
            this.clients = new Map();
        }

        creds() {
            return this.profile ? fromSSO({ profile: this.profile }) : undefined;
        }

        get(ClientClass, key) {
            if (!this.clients.has(key)) {
            this.clients.set(key, new ClientClass({
                region: this.region,
                credentials: this.creds()
            }));
            }
            return this.clients.get(key);
        }

        s3()       { return this.get(S3Client, 's3'); }
        dynamodb() { return this.get(DynamoDBClient, 'dynamodb'); }
        }

        // Usage
        export const aws = new AWSConfig();
        export const s3Client = aws.s3();
        export const dynamoDBClient = aws.dynamodb();
    ```
  </Tab>
</Tabs>

### Environment Configuration

```bash theme={null}
# Set environment variables
export NODE_ENV=development
export AWS_PROFILE=<project>-<production_workload_account>
export AWS_REGION=us-west-2

# Run your Node.js application
npm start
```

## Go Example

For Go applications, AWS provides excellent SSO support through the official SDK.

### Installation

```bash theme={null}
go get github.com/aws/aws-sdk-go-v2/aws
go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/service/s3
```

### Implementation

<Tabs>
  <Tab title="Basic Configuration">
    ```go theme={null}
    import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/aws/aws-sdk-go-v2/aws"
        "github.com/aws/aws-sdk-go-v2/config"
        "github.com/aws/aws-sdk-go-v2/service/s3"
    )

    func newConfig(ctx context.Context) (aws.Config, error) {
        profile := os.Getenv("AWS_PROFILE")
        region  := os.Getenv("AWS_REGION")
        if profile != "" {
            return config.LoadDefaultConfig(ctx,
                config.WithSharedConfigProfile(profile),
                config.WithRegion(region),
            )
        }
        // Cloud: default chain (role)
        return config.LoadDefaultConfig(ctx, config.WithRegion(region))
    }

    func main() {
        ctx := context.Background()
        cfg, err := newConfig(ctx)
        if err != nil {
            log.Fatalf("unable to load SDK config: %v", err)
        }
        s3c := s3.NewFromConfig(cfg)
        out, err := s3c.ListBuckets(ctx, &s3.ListBucketsInput{})
        if err != nil {
            log.Fatalf("list buckets failed: %v", err)
        }
        for _, b := range out.Buckets {
            fmt.Println(*b.Name)
        }
    }
    ```
  </Tab>

  <Tab title="Advanced Configuration">
    ```go theme={null}
    package aws

    import (
        "context"
        "os"

        "github.com/aws/aws-sdk-go-v2/aws"
        "github.com/aws/aws-sdk-go-v2/config"
        "github.com/aws/aws-sdk-go-v2/service/dynamodb"
        "github.com/aws/aws-sdk-go-v2/service/s3"
        "github.com/aws/aws-sdk-go-v2/service/ses"
    )

    type Cfg struct{ cfg aws.Config }

    func New(ctx context.Context, regionDefault string) (*Cfg, error) {
        region  := envOr("AWS_REGION", regionDefault)
        profile := os.Getenv("AWS_PROFILE")

        opts := []func(*config.LoadOptions) error{ config.WithRegion(region) }
        if profile != "" {
            opts = append(opts, config.WithSharedConfigProfile(profile))
        }

        c, err := config.LoadDefaultConfig(ctx, opts...)
        if err != nil { return nil, err }
        return &Cfg{cfg: c}, nil
    }

    func (c *Cfg) S3() *s3.Client             { return s3.NewFromConfig(c.cfg) }
    func (c *Cfg) DynamoDB() *dynamodb.Client { return dynamodb.NewFromConfig(c.cfg) }
    func (c *Cfg) SES() *ses.Client           { return ses.NewFromConfig(c.cfg) }

    func envOr(k, d string) string {
        if v := os.Getenv(k); v != "" { return v }
        return d
    }
    ```
  </Tab>
</Tabs>

## Development Workflow Best Practices

### Environment Setup

Use environment-specific files and variables.

<Tabs>
  <Tab title=".env.local">
    ```bash theme={null}
    # Local development 
    NODE_ENV=development
    AWS_PROFILE=<project>-<development_workload_account>
    AWS_REGION=us-west-2

    # APP settings
    LOG_LEVEL=debug
    API_BASE_URL=https://staging-api.<project>.com
    ```
  </Tab>

  <Tab title=".env.production">
    ```bash theme={null}
    # AWS Cloud environment 
    NODE_ENV=production
    # AWS_PROFILE not set - uses IAM roles
    AWS_REGION=us-west-2

    # Application settings
    LOG_LEVEL=info
    API_BASE_URL=https://api.<project>.com
    ```
  </Tab>
</Tabs>

## Common Patterns and Tips

<AccordionGroup>
  <Accordion title="Environment Detection" icon="search">
    **Best practices for detecting local vs. cloud environments:**

    ```python theme={null}
    import os,pathlib

    def is_local_environment():
        return any([
            os.getenv('AWS_PROFILE'),
            os.getenv('NODE_ENV') == 'development',
            pathlib.Path('.env.local').exists()
        ])
    ```
  </Accordion>

  <Accordion title="Error Handling" icon="exclamation-triangle">
    **Handle common SSO-related errors gracefully:**

    ```python theme={null}
    from botocore.exceptions import TokenRefreshRequired, ProfileNotFound

    def handle_aws_errors(func):
        """Decorator for AWS error handling"""
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except TokenRefreshRequired:
                print("SSO token expired. Please run: aws sso login")
                raise
            except ProfileNotFound as e:
                print(f"AWS profile not found: {e}")
                raise
            except Exception as e:
                print(f"AWS operation failed: {e}")
                raise
        return wrapper
    ```
  </Accordion>

  <Accordion title="Configuration Validation" icon="check-circle">
    **Validate your configuration before running:**

    ```python theme={null}
    def validate_aws_config():
        """Validate AWS configuration"""
        profile = os.getenv('AWS_PROFILE')
        if not profile:
            raise ValueError("AWS_PROFILE environment variable not set")
        
        # Test connection
        try:
            boto3.client('sts').get_caller_identity()
        except Exception as e:
            raise ValueError(f"AWS configuration invalid: {e}")
        
        return True
    ```
  </Accordion>
</AccordionGroup>
