Skip to content

Instantly share code, notes, and snippets.

@exonomyapp
Last active September 10, 2024 14:29
Show Gist options
  • Save exonomyapp/cfbe6bd7ea144f1bc3cee4702d0f31e7 to your computer and use it in GitHub Desktop.
Save exonomyapp/cfbe6bd7ea144f1bc3cee4702d0f31e7 to your computer and use it in GitHub Desktop.
Layers and Modularity in Nuxt Authentication

To create a comprehensive Nuxt Layer strategy that supports various authentication providers and storage/database solutions, we need a well-organized folder and file structure that separates concerns, allows for easy extension, and maintains modularity. Below is a recommended folder structure and file organization:

Project Root

/my-nuxt-app
β”‚
β”œβ”€β”€ /app/                 	# Primary application layer
β”‚   β”œβ”€β”€ /components/      	# Reusable Vue components
β”‚   β”œβ”€β”€ /layouts/         	# Application layouts
β”‚   β”œβ”€β”€ /pages/           	# Application pages
β”‚   β”œβ”€β”€ /plugins/         	# Global plugins and integrations
β”‚   β”œβ”€β”€ /middleware/      	# Middleware functions
β”‚   β”œβ”€β”€ /composables/     	# Reusable composable functions (e.g., useAuth, useDB)
β”‚   β”œβ”€β”€ /stores/          	# Pinia stores or Vuex modules
β”‚   β”œβ”€β”€ /assets/          	# Static assets (images, fonts, etc.)
β”‚   β”œβ”€β”€ /utils/           	# Utility functions
β”‚   β”œβ”€β”€ /modules/         	# Nuxt-specific modules for app customization
β”‚   └── nuxt.config.ts    	# Main Nuxt configuration
β”‚
β”œβ”€β”€ /layers/              	# Nuxt Layers directory
β”‚   β”œβ”€β”€ /auth/            	# Authentication Layer
β”‚   β”‚   β”œβ”€β”€ /providers/   	# Various authentication providers
β”‚   β”‚   β”‚   β”œβ”€β”€ /oauth/   	# OAuth providers (Google, Facebook, etc.)
β”‚   β”‚   β”‚   β”œβ”€β”€ /email/   	# Email/Password-based auth
β”‚   β”‚   β”‚   β”œβ”€β”€ /social/  	# Social media auth (Twitter, LinkedIn, etc.)
β”‚   β”‚   β”‚   └── index.ts  	# Authentication providers export
β”‚   β”‚   β”œβ”€β”€ /composables/ 	# Authentication-specific composables (e.g., useAuth)
β”‚   β”‚   β”œβ”€β”€ /middleware/  	# Authentication middleware (e.g., route guards)
β”‚   β”‚   β”œβ”€β”€ /plugins/     	# Auth-related plugins (e.g., JWT, OAuth)
β”‚   β”‚   β”œβ”€β”€ /stores/      	# Auth-related state management (e.g., user store)
β”‚   β”‚   └── nuxt.config.ts	# Auth layer-specific Nuxt config
β”‚   β”‚
β”‚   β”œβ”€β”€ /storage/         	# Storage/Database Layer
β”‚   β”‚   β”œβ”€β”€ /databases/   	# Database connectors (SQL, NoSQL, etc.)
β”‚   β”‚   β”‚   β”œβ”€β”€ /sql/     	# SQL databases (PostgreSQL, MySQL, etc.)
β”‚   β”‚   β”‚   β”œβ”€β”€ /nosql/   	# NoSQL databases (MongoDB, Firebase, etc.)
β”‚   β”‚   β”‚   β”œβ”€β”€ /orm/     	# ORM connectors (Prisma, TypeORM, etc.)
β”‚   β”‚   β”‚   └── index.ts  	# Databases export
β”‚   β”‚   β”œβ”€β”€ /composables/ 	# Database-specific composables (e.g., useDB)
β”‚   β”‚   β”œβ”€β”€ /plugins/     	# Database-related plugins (e.g., Prisma, Mongoose)
β”‚   β”‚   └── nuxt.config.ts	# Storage layer-specific Nuxt config
β”‚   β”‚
β”‚   └── /common/          	# Common Layer for shared utilities and configurations
β”‚   	β”œβ”€β”€ /utils/       	# Common utility functions
β”‚   	β”œβ”€β”€ /composables/ 	# Shared composables (e.g., useFetch)
β”‚   	β”œβ”€β”€ /plugins/     	# Shared plugins
β”‚   	β”œβ”€β”€ /stores/      	# Shared state management
β”‚   	└── nuxt.config.ts	# Common layer-specific Nuxt config
β”‚
└── package.json          	# Project dependencies

Detailed Explanation:

/app/ Directory

This is where our primary application code lives, including pages, layouts, components, and the main Nuxt configuration file. It serves as the core of our application.

/layers/ Directory

The /layers/ directory holds the Nuxt Layers for authentication, storage, and other common utilities. Each layer is isolated and can be composed or reused in other projects.

  • /auth/ Layer:

    • /providers/: Contains subfolders for each type of authentication provider (OAuth, email/password, social).
    • /composables/: Houses authentication-specific composables, such as useAuth.
    • /middleware/: Middleware functions for route protection and authentication checks.
    • /plugins/: Plugins for handling authentication logic (e.g., JWT, OAuth2).
    • /stores/: State management for authentication (e.g., managing user sessions).
  • /storage/ Layer:

    • /databases/: Contains subfolders for different database types (SQL, NoSQL, ORM connectors).
    • /composables/: Composables specific to database interactions, like useDB.
    • /plugins/: Plugins related to database integrations (e.g., Prisma, Mongoose).
  • /common/ Layer:

    • /utils/: Common utility functions that can be used across different layers.
    • /composables/: Shared composables that are not specific to any layer but provide common functionality.
    • /plugins/: Plugins that might be needed by multiple layers or the main app.
    • /stores/: Shared state management for data that is needed across the entire app.

Layer Configuration (nuxt.config.ts Files)

Each layer has its own nuxt.config.ts file, allowing us to define layer-specific configurations. This keeps our main nuxt.config.ts file clean and makes it easier to manage complex setups.

Export Strategy

Each layer exports its functionalities via an index.ts file in the root of the layer. This allows for easy imports and compositions in our main application or other layers.

Example Usage in App Layer:

To use these layers in our main app, we might import them as follows:

// nuxt.config.ts in /app/
import authLayer from '../layers/auth/nuxt.config';
import storageLayer from '../layers/storage/nuxt.config';
import commonLayer from '../layers/common/nuxt.config';

export default defineNuxtConfig({
  ...commonLayer,
  ...authLayer,
  ...storageLayer,
  // other app-specific configurations
});

This structure ensures that our application remains modular, scalable, and maintainable as we add more authentication providers, storage options, and other features.

In the structure described, the lowest level layer for authentication would typically be the /providers/ directory as a whole, rather than each individual subfolder like /Facebook/, /Twitter/, /GitHub/, etc., being a layer of its own. The /providers/ directory collectively represents a layer that manages various authentication providers.

Here's why:

  1. Modularity and Maintainability:

    • Treating the entire /providers/ directory as one layer ensures that all authentication logic is managed in a unified manner. It avoids the complexity of having to load and manage multiple layers individually, which could lead to unnecessary complexity and overhead.
  2. Shared Logic:

    • Many authentication mechanisms (such as handling tokens, user sessions, or redirects) are shared across providers. By keeping all providers within a single layer, we can centralize shared logic and configurations (e.g., OAuth handling) and avoid redundant code.
  3. Ease of Composition:

    • When we include the auth layer in our main app, we typically want to include all authentication options at once. By having a single layer at the /providers/ level, we can easily compose and configure all providers together without needing to manage multiple layers.

Structure Recap with /providers/ as the Layer:

/layers/
β”œβ”€β”€ /auth/            	# Authentication Layer
β”‚   β”œβ”€β”€ /providers/   	# Contains all authentication providers
β”‚   β”‚   β”œβ”€β”€ /oauth/   	# OAuth-based providers (Google, Facebook, etc.)
β”‚   β”‚   β”‚   β”œβ”€β”€ /Google/
β”‚   β”‚   β”‚   β”œβ”€β”€ /Facebook/
β”‚   β”‚   β”‚   └── /GitHub/
β”‚   β”‚   β”œβ”€β”€ /email/   	# Email/Password-based auth
β”‚   β”‚   β”œβ”€β”€ /social/  	# Social media providers (Twitter, LinkedIn, etc.)
β”‚   β”‚   └── index.ts  	# Central export for providers
β”‚   β”œβ”€β”€ /composables/ 	# Authentication-specific composables (e.g., useAuth)
β”‚   β”œβ”€β”€ /middleware/  	# Authentication middleware (e.g., route guards)
β”‚   β”œβ”€β”€ /plugins/     	# Auth-related plugins (e.g., JWT, OAuth)
β”‚   β”œβ”€β”€ /stores/      	# Auth-related state management (e.g., user store)
β”‚   └── nuxt.config.ts	# Auth layer-specific Nuxt config

How It Works:

  • /providers/index.ts: This file can export all available providers so that the main auth layer can be easily composed and used in our Nuxt app.

  • Shared Logic: Common authentication logic and utilities (e.g., token handling, API calls) can be placed at the /auth/ layer level, accessible by all providers within the /providers/ directory.

  • Adding New Providers: To add a new provider (e.g., Apple, LinkedIn), we simply create a new subfolder within /providers/, add the necessary files, and include it in the central export without having to create an entirely new layer.

This approach strikes a balance between modularity and simplicity, ensuring that our authentication setup is powerful yet easy to manage.

Templated OAuth Provider Onboarding:

We want to support as many OAuth providers as possible, so we'll zoom in on the OAuth providers level of our authentication file/folder model and propose a structure that can be easily replicated and reused for quickly adding new authenticators on demand. Here's how a typical folder would be organized.

Each folder inside the /oauth/ directory represents a specific OAuth provider (e.g., Google, Facebook, GitHub). The structure of each provider's folder would be consistent to ensure maintainability and to follow a standard pattern for adding or modifying OAuth providers. Below is the anatomy of each folder within the /oauth/ directory.

Anatomy of an OAuth Provider Folder (/oauth/{ProviderName}/)

/oauth/
β”œβ”€β”€ /{ProviderName}/           # Folder for a specific OAuth provider (e.g., Google, Facebook, GitHub)
β”‚   β”œβ”€β”€ /composables/          # Composables specific to this provider
β”‚   β”‚   └── use{ProviderName}Auth.ts  # Composable for handling authentication logic
β”‚   β”‚
β”‚   β”œβ”€β”€ /middleware/           # Middleware specific to this provider
β”‚   β”‚   └── {providerName}AuthGuard.ts # Middleware for route protection or redirects
β”‚   β”‚
β”‚   β”œβ”€β”€ /services/             # Services for API calls and interactions
β”‚   β”‚   └── {providerName}API.ts # Service for interacting with provider's API (e.g., token exchange)
β”‚   β”‚
β”‚   β”œβ”€β”€ /stores/               # Store for provider-specific state management
β”‚   β”‚   └── {providerName}Store.ts # State management for user session, tokens, etc.
β”‚   β”‚
β”‚   β”œβ”€β”€ config.ts              # Provider-specific configuration (e.g., client ID, redirect URI)
β”‚   β”œβ”€β”€ constants.ts           # Provider-specific constants (e.g., endpoints, scopes)
β”‚   β”œβ”€β”€ routes.ts              # Optional: Routes related to OAuth (e.g., callback routes)
β”‚   └── index.ts               # Main export file for this provider's functionality

Explanation of Each File and Folder:

  1. /composables/

    • use{ProviderName}Auth.ts: This composable handles the authentication flow specific to the provider, such as initiating login, handling callback responses, and storing tokens.
    • Example: useGoogleAuth.ts might include methods like loginWithGoogle(), handleGoogleCallback(), etc.
  2. /middleware/

    • {providerName}AuthGuard.ts: Middleware to protect routes or handle redirections based on the user's authentication state with the provider.
    • Example: googleAuthGuard.ts could check if a user is authenticated with Google before allowing access to certain routes.
  3. /services/

    • {providerName}API.ts: This file manages the interaction with the provider’s API, such as exchanging authorization codes for tokens, refreshing tokens, and fetching user data.
    • Example: googleAPI.ts might contain methods for calling Google's token endpoint and user info endpoint.
  4. /stores/

    • {providerName}Store.ts: Manages state related to the provider, like storing tokens, user information, and session data. It can use Pinia or Vuex depending on your state management choice.
    • Example: googleStore.ts could store the Google access token, refresh token, and user profile.
  5. config.ts

    • Contains provider-specific configuration, such as OAuth client ID, client secret, redirect URIs, scopes, and any other settings required by the provider.
    • Example: For Google, this might include clientID, clientSecret, redirectURI, and scopes like email, profile.
  6. constants.ts

    • Defines constants used across the provider's logic, such as endpoint URLs, authorization URLs, token URLs, and commonly used scopes.
    • Example: For GitHub, this could include GITHUB_AUTH_URL, GITHUB_TOKEN_URL, and SCOPES.
  7. routes.ts (Optional)

    • If the provider requires specific routes for handling OAuth callbacks, these routes can be defined here.
    • Example: routes.ts might define a callback route like /auth/google/callback and handle the processing of the OAuth response.
  8. index.ts

    • This file serves as the main entry point for the provider's functionality. It typically re-exports everything from the other files, making it easier to import the provider's logic elsewhere in the application.
    • Example: index.ts could export useGoogleAuth, googleAPI, and any other relevant utilities or constants.

Common Information Across All Providers:

  • OAuth Flow: The basic OAuth flow (authorization code grant, token exchange, etc.) is common across all providers, though the specifics (endpoints, scopes) differ.
  • State Management: Each provider typically requires state management for tokens and user information, which can be handled in the same way across providers but with provider-specific details.
  • API Interaction: Interaction with the provider's API for tasks like fetching user profiles or refreshing tokens is a common requirement.
  • Configuration: Each provider needs configuration settings like client ID, secret, and redirect URIs.

Example: /oauth/Google/

/oauth/
β”œβ”€β”€ /Google/
β”‚   β”œβ”€β”€ /composables/
β”‚   β”‚   └── useGoogleAuth.ts
β”‚   β”œβ”€β”€ /middleware/
β”‚   β”‚   └── googleAuthGuard.ts
β”‚   β”œβ”€β”€ /services/
β”‚   β”‚   └── googleAPI.ts
β”‚   β”œβ”€β”€ /stores/
β”‚   β”‚   └── googleStore.ts
β”‚   β”œβ”€β”€ config.ts
β”‚   β”œβ”€β”€ constants.ts
β”‚   β”œβ”€β”€ routes.ts
β”‚   └── index.ts

This structure ensures that each OAuth provider is self-contained, with clear separation of concerns, while still following a consistent pattern across different providers. This approach makes it easier to add new providers, maintain existing ones, and understand the flow of authentication in your application.

Additional Considerations for OAuth Providers

OAuth authentication providers allow users to log in to a service using their credentials from another platform, offering both convenience and security. Every OAuth provider we support has their own distinct features and we'll be prepared to leverage them as well as capture the data that is sometimes unique to their authentication flow. Here's a list of the top 10 most popular OAuth authentication providers:

1. Google

  • Use Cases: Widely used across web and mobile applications for both user authentication and API access.
  • Strengths: Extensive integration with Google services, high reliability, and strong security features.

2. Facebook

  • Use Cases: Commonly used in social networking and apps that rely on social interactions.
  • Strengths: Easy access to user profile information, including social graphs.

3. GitHub

  • Use Cases: Popular in the developer community for accessing GitHub repositories and integrating with development tools.
  • Strengths: Strong developer support, extensive API, and integration with code hosting services.

4. Apple (Sign in with Apple)

  • Use Cases: Primarily used in iOS and macOS applications for user authentication.
  • Strengths: Focus on privacy, anonymous email relay, and strong security features.

5. Microsoft

  • Use Cases: Used in enterprise applications, especially those integrating with Office 365, Azure, and other Microsoft services.
  • Strengths: Strong enterprise support, integration with Microsoft’s ecosystem, and Active Directory.

6. Amazon

  • Use Cases: Used in applications that integrate with Amazon’s services, including AWS and Amazon's consumer platforms.
  • Strengths: Integration with Amazon services, access to consumer data (e.g., purchase history).

7. Twitter

  • Use Cases: Common in social media applications and services that integrate with Twitter’s API.
  • Strengths: Access to user’s social graph and real-time data from Twitter.

8. LinkedIn

  • Use Cases: Popular in professional networking apps and services focused on career development.
  • Strengths: Access to professional profiles, connections, and job-related data.

9. Spotify

  • Use Cases: Primarily used in music-related applications and services that integrate with Spotify’s platform.
  • Strengths: Access to user’s music preferences, playlists, and listening history.

10. PayPal

  • Use Cases: Commonly used in e-commerce applications where users need to log in to access payment methods.
  • Strengths: Integration with payment systems, secure access to user’s financial information.

Additional Considerations:

  • Okta and Auth0 (now owned by Okta) are also popular in enterprise contexts, providing robust identity management solutions that include OAuth among other authentication methods.
  • Salesforce is another notable provider, particularly in CRM and enterprise solutions.

These providers are popular due to their vast user bases, extensive APIs, and strong security features, making them reliable choices for OAuth authentication in various applications.

@exonomyapp
Copy link
Author

The following information applies to the app that we are going to build, which is decentralized, so it has no back end configuration for the server, since we have no servers in a P2P app:

Here’s the modified version of our folder structure with backend-related parts removed for P2P architecture:

/my-nuxt-app
β”‚
β”œβ”€β”€ /app/                 
β”‚   β”œβ”€β”€ /components/      	# Reusable Vue components
β”‚   β”œβ”€β”€ /layouts/         	# Application layouts
β”‚   β”œβ”€β”€ /pages/           	# Application pages
β”‚   β”œβ”€β”€ /plugins/         	# Global plugins and integrations (e.g., SQLite3)
β”‚   β”œβ”€β”€ /middleware/      	# Middleware functions
β”‚   β”œβ”€β”€ /composables/     	# Reusable composables (e.g., useDB)
β”‚   β”œβ”€β”€ /stores/          	# Pinia stores or Vuex modules
β”‚   β”œβ”€β”€ /assets/          	# Static assets (images, fonts, etc.)
β”‚   β”œβ”€β”€ /utils/           	# Utility functions
β”‚   └── nuxt.config.ts    	# Main Nuxt configuration
β”‚
β”œβ”€β”€ /layers/              
β”‚   β”œβ”€β”€ /auth/             
β”‚   β”‚   β”œβ”€β”€ /providers/    
β”‚   β”‚   β”‚   β”œβ”€β”€ /oauth/   	# OAuth providers (Google, Facebook, etc.)
β”‚   β”‚   β”‚   β”œβ”€β”€ /email/   	# Email/Password-based auth
β”‚   β”‚   β”‚   β”œβ”€β”€ /social/  	# Social media auth (Twitter, LinkedIn, etc.)
β”‚   β”‚   β”‚   └── index.ts  	# Authentication providers export
β”‚   β”‚   β”œβ”€β”€ /composables/ 	# Authentication-specific composables
β”‚   β”‚   β”œβ”€β”€ /middleware/  	# Authentication middleware (e.g., route guards)
β”‚   β”‚   β”œβ”€β”€ /stores/      	# Auth-related state management
β”‚   β”‚   └── nuxt.config.ts	# Auth layer-specific Nuxt config
β”‚   β”œβ”€β”€ /storage/          
β”‚   β”‚   β”œβ”€β”€ /databases/    
β”‚   β”‚   β”‚   β”œβ”€β”€ /sql/     	# SQL databases (SQLite3)
β”‚   β”‚   β”œβ”€β”€ /composables/ 	# Database-specific composables
β”‚   β”‚   └── nuxt.config.ts	# Storage layer-specific Nuxt config
β”‚
└── package.json          	# Project dependencies

Key Point:

Auth Providers Subfolders (/oauth, /email, /social) are there but not because we have a server against which our Exonomists must authenticate. Rather, they might want to offer us a token from their centralized authenticator so that we can include their data when they are using our app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment