### AllyManager Helper Methods for Provider Configuration Source: https://context7.com/adonisjs/ally/llms.txt Utilize AllyManager helper methods to check provider configurations and capabilities. This includes getting all configured provider names, checking if a specific provider is configured, listing providers that allow local signup, and verifying if a provider allows local signup. ```typescript import router from '@adonisjs/core/services/router' router.get('/auth/providers', async ({ ally }) => { // Get all configured provider names const providers = ally.configuredProviderNames() // Returns: ['github', 'google', 'discord', ...] // Check if a specific provider is configured if (ally.has('github')) { console.log('GitHub is configured') } // Get providers that allow local signup const signupProviders = ally.signupProviderNames() // Returns providers where disallowLocalSignup is not true // Check if a provider allows local signup const canSignupWithGithub = ally.allowsLocalSignup('github') return { all: providers, signup: signupProviders, github: { configured: ally.has('github'), allowsSignup: ally.allowsLocalSignup('github'), }, } }) ``` ```typescript // Dynamic provider selection with validation router.get('/auth/:provider/redirect', async ({ ally, params, response }) => { const provider = params.provider // Validate provider exists if (!ally.has(provider)) { return response.notFound(`Unknown provider: ${provider}`) } // Check signup permissions for signup flow if (!ally.allowsLocalSignup(provider)) { return response.forbidden(`Signup not allowed with ${provider}`) } return ally.use(provider).redirect() }) ``` -------------------------------- ### AllyManager.use() - Get Provider Driver Instance Source: https://context7.com/adonisjs/ally/llms.txt The `ally.use()` method returns a driver instance for the specified social provider. The driver is cached for the duration of the HTTP request to avoid creating duplicate instances. ```APIDOC ## GET /auth/:provider/redirect ### Description Initiates the OAuth flow by redirecting the user to the social provider's authorization page. ### Method GET ### Endpoint /auth/:provider/redirect ### Parameters #### Path Parameters - **provider** (string) - Required - The name of the social provider (e.g., github, google). ### Request Example ```typescript router.get('/auth/:provider/redirect', async ({ ally, params }) => { const driver = ally.use(params.provider) return driver.redirect() }) ``` ## GET /auth/:provider/callback ### Description Handles the callback from the social provider after user authentication. It retrieves user information and checks for potential errors during the authentication process. ### Method GET ### Endpoint /auth/:provider/callback ### Parameters #### Path Parameters - **provider** (string) - Required - The name of the social provider. #### Query Parameters - **error** (string) - Optional - Indicates an error during the OAuth flow. - **code** (string) - Optional - The authorization code received from the provider. - **state** (string) - Optional - The state parameter used for CSRF protection. ### Response #### Success Response (200) - **id** (string) - The unique identifier of the authenticated user. - **name** (string) - The name of the authenticated user. - **email** (string) - The email address of the authenticated user. - **avatarUrl** (string) - The URL of the user's avatar. - **token** (object) - Authentication tokens (e.g., access token, refresh token). #### Response Example ```json { "id": "user_id_123", "name": "John Doe", "email": "john.doe@example.com", "avatarUrl": "https://example.com/avatar.jpg", "token": { "accessToken": "...", "refreshToken": "..." } } ``` ## GET /signup/:provider ### Description Initiates the OAuth flow with the intent to sign up a new user. This method may throw an `E_LOCAL_SIGNUP_DISALLOWED` error if the provider is configured to disallow local signups. ### Method GET ### Endpoint /signup/:provider ### Parameters #### Path Parameters - **provider** (string) - Required - The name of the social provider. ### Request Example ```typescript router.get('/signup/:provider', async ({ ally, params }) => { return ally.use(params.provider, { intent: 'signup' }).redirect() }) ``` ``` -------------------------------- ### Get Provider Driver Instance with Ally.use() Source: https://context7.com/adonisjs/ally/llms.txt Retrieve a cached driver instance for a specific social provider during an HTTP request. Handles redirect and callback logic for authentication flows. ```typescript import router from '@adonisjs/core/services/router' router.get('/auth/:provider/redirect', async ({ ally, params }) => { // Get driver instance for the specified provider const driver = ally.use(params.provider) // Redirect user to provider's authorization page return driver.redirect() }) router.get('/auth/:provider/callback', async ({ ally, params, response }) => { const driver = ally.use(params.provider) // Check for errors before processing if (driver.accessDenied()) { return response.badRequest('Access was denied by the user') } if (driver.stateMisMatch()) { return response.badRequest('Request expired. Please try again') } if (driver.hasError()) { return response.badRequest(driver.getError()) } // Get authenticated user const user = await driver.user() return { id: user.id, name: user.name, email: user.email, avatarUrl: user.avatarUrl, token: user.token, } }) ``` ```typescript // Use with intent option for signup/login flows router.get('/signup/:provider', async ({ ally, params }) => { // Will throw E_LOCAL_SIGNUP_DISALLOWED if provider has disallowLocalSignup: true return ally.use(params.provider, { intent: 'signup' }).redirect() }) ``` -------------------------------- ### Get Authenticated User Profile (driver.user()) Source: https://context7.com/adonisjs/ally/llms.txt Exchanges the authorization code for an access token and fetches the user's profile from the provider. Returns a standardized user object with provider-specific data in the `original` property. ```APIDOC ## GET /github/callback ### Description Handles the callback from GitHub OAuth, exchanges the authorization code for an access token, fetches the user's profile, and either finds or creates a user in the database before logging them in. ### Method GET ### Endpoint /github/callback ### Parameters #### Query Parameters - **code** (string) - Required - The authorization code returned by GitHub. - **state** (string) - Required - The state parameter used to prevent CSRF attacks. ### Request Body None ### Response #### Success Response (200) Redirects to '/dashboard' upon successful login. #### Response Example Redirects to '/dashboard' ### Error Handling - **access_denied**: User denied access to their GitHub profile. - **state_mismatch**: State parameter mismatch, indicating a potential CSRF attack. - **error**: Other OAuth errors from GitHub. ``` -------------------------------- ### Get User from Existing Access Token (driver.userFromToken()) Source: https://context7.com/adonisjs/ally/llms.txt Fetches user profile using an existing access token. Useful for verifying tokens or refreshing user data without re-authenticating. Only available for OAuth2 providers. ```APIDOC ## GET /api/user/refresh ### Description Refreshes the authenticated user's profile data from GitHub using their stored access token. ### Method GET ### Endpoint /api/user/refresh ### Parameters None (relies on authenticated user session and stored token) ### Request Body None ### Response #### Success Response (200) - **id** (string) - Provider's unique user ID. - **name** (string) - Display name of the user. - **email** (string) - Email address of the user. - **avatarUrl** (string) - URL of the user's avatar. #### Response Example { "id": "12345", "name": "The Octocat", "email": "octocat@github.com", "avatarUrl": "https://avatars.githubusercontent.com/u/12345" } #### Error Response (400) - **error** (string) - Message indicating the error, e.g., 'No GitHub token stored'. #### Error Response Example { "error": "No GitHub token stored" } #### Error Response (500) - **error** (string) - Message indicating failure to fetch profile. - **details** (string) - Specific error message from the provider. #### Error Response Example { "error": "Failed to fetch user profile", "details": "Token expired" } ``` ```APIDOC ## POST /api/auth/verify-token ### Description Verifies an external OAuth token provided by a client (e.g., a mobile app) and returns user information if the token is valid. ### Method POST ### Endpoint /api/auth/verify-token ### Parameters #### Request Body - **provider** (string) - Required - The OAuth provider (e.g., 'github', 'google'). - **token** (string) - Required - The access token to verify. ### Request Example { "provider": "github", "token": "gho_xxxx" } ### Response #### Success Response (200) - **valid** (boolean) - Indicates if the token is valid. - **user** (object) - User information if the token is valid. - **id** (string) - Provider's unique user ID. - **email** (string) - Email address of the user. - **name** (string) - Display name of the user. #### Response Example { "valid": true, "user": { "id": "12345", "email": "octocat@github.com", "name": "The Octocat" } } #### Error Response (200) - **valid** (boolean) - Indicates if the token is valid (will be false). - **error** (string) - Error message if the token is invalid or verification fails. #### Response Example { "valid": false, "error": "Invalid token" } ``` -------------------------------- ### Get Access Token Only Source: https://context7.com/adonisjs/ally/llms.txt Exchanges an authorization code for an access token without fetching the user profile. This is useful when only the token is needed for subsequent API calls. ```APIDOC ## GET /google/callback ### Description Handles the callback from Google OAuth2, exchanges the authorization code for an access token, and optionally fetches user data. ### Method GET ### Endpoint /google/callback ### Parameters #### Query Parameters - **code** (string) - Required - The authorization code received from Google. - **state** (string) - Optional - The state parameter for CSRF protection. ### Response #### Success Response (200) - **accessToken** (string) - The obtained OAuth2 access token. - **expiresAt** (string) - The ISO string representation of the token's expiration time. - **refreshToken** (string) - The refresh token, if offline access was granted. - **events** (object) - The response from a sample Google Calendar API call. #### Response Example ```json { "accessToken": "ya29.xxx", "expiresAt": "2023-10-27T10:00:00.000Z", "refreshToken": "1//xxx", "events": { "kind": "calendar#events", "etag": "...", "summary": "primary", "updated": "...", "timeZone": "UTC", "accessRole": "owner", "defaultReminders": [], "items": [] } } ``` ``` -------------------------------- ### driver.redirectUrl() - Get Authorization URL Without Redirect Source: https://context7.com/adonisjs/ally/llms.txt Returns the authorization URL without performing the redirect. Useful for APIs, SPAs, or when you need to handle the redirect manually. ```APIDOC ## GET /api/auth/github/url ### Description Generates the authorization URL for GitHub without performing a redirect, allowing for manual handling. ### Method GET ### Endpoint /api/auth/github/url ### Parameters #### Query Parameters - **scopes** (string[]) - Optional - An array of scopes to request from the provider. ### Response #### Success Response (200) - **redirectUrl** (string) - The generated authorization URL. ### Request Example ```typescript router.get('/api/auth/github/url', async ({ ally }) => { const url = await ally.use('github').stateless().redirectUrl((request) => { request.scopes(['user:email']) }) return { redirectUrl: url } }) ``` ## GET /api/auth/urls ### Description Generates authorization URLs for multiple OAuth providers simultaneously. ### Method GET ### Endpoint /api/auth/urls ### Response #### Success Response (200) - **github** (string) - The authorization URL for GitHub. - **google** (string) - The authorization URL for Google. - **discord** (string) - The authorization URL for Discord. ### Response Example ```json { "github": "https://github.com/login/oauth/authorize?...", "google": "https://accounts.google.com/o/oauth2/v2/auth?...", "discord": "https://discord.com/oauth2/authorize?..." } ``` ### Request Example ```typescript router.get('/api/auth/urls', async ({ ally }) => { const [githubUrl, googleUrl, discordUrl] = await Promise.all([ ally.use('github').stateless().redirectUrl(), ally.use('google').stateless().redirectUrl(), ally.use('discord').stateless().redirectUrl(), ]) return { github: githubUrl, google: googleUrl, discord: discordUrl, } }) ``` ``` -------------------------------- ### Get User from OAuth1 Token Source: https://context7.com/adonisjs/ally/llms.txt Fetches user profile using an existing OAuth1 access token and secret. This method is specifically for OAuth1 providers such as Twitter. ```APIDOC ## POST /api/auth/twitter/verify ### Description Verifies Twitter OAuth1 credentials using a provided access token and secret to fetch user information. ### Method POST ### Endpoint /api/auth/twitter/verify ### Parameters #### Request Body - **token** (string) - Required - The OAuth1 access token. - **secret** (string) - Required - The OAuth1 access token secret. ### Request Example ```json { "token": "your_oauth1_token", "secret": "your_oauth1_secret" } ``` ### Response #### Success Response (200) - **valid** (boolean) - Indicates if the credentials are valid. - **user** (object) - Contains user profile information if valid. - **id** (string) - The user's unique identifier. - **screenName** (string) - The user's screen name. - **name** (string) - The user's display name. - **avatarUrl** (string) - The URL of the user's avatar. #### Response Example ```json { "valid": true, "user": { "id": "123456789", "screenName": "twitterUser", "name": "Twitter User Name", "avatarUrl": "https://example.com/avatar.jpg" } } ``` ## GET /api/user/twitter/refresh ### Description Refreshes a user's Twitter profile data from stored OAuth1 credentials. ### Method GET ### Endpoint /api/user/twitter/refresh ### Response #### Success Response (200) - **success** (boolean) - Indicates if the refresh was successful. - **user** (object) - The refreshed user data from Twitter. #### Response Example ```json { "success": true, "user": { "id": "123456789", "name": "Updated Name", "avatarUrl": "https://example.com/new_avatar.jpg" } } ``` ``` -------------------------------- ### Get Authorization URL Without Redirect with driver.redirectUrl() Source: https://context7.com/adonisjs/ally/llms.txt Generates the OAuth authorization URL without performing an immediate redirect. Useful for SPAs, APIs, or manual redirect handling. Supports custom scopes and stateless operations. ```typescript import router from '@adonisjs/core/services/router' // Get URL for manual redirect handling router.get('/api/auth/github/url', async ({ ally }) => { const url = await ally.use('github').stateless().redirectUrl((request) => { request.scopes(['user:email']) }) return { redirectUrl: url } }) ``` ```typescript // Generate multiple provider URLs router.get('/api/auth/urls', async ({ ally }) => { const [githubUrl, googleUrl, discordUrl] = await Promise.all([ ally.use('github').stateless().redirectUrl(), ally.use('google').stateless().redirectUrl(), ally.use('discord').stateless().redirectUrl(), ]) return { github: githubUrl, google: googleUrl, discord: discordUrl, } }) ``` -------------------------------- ### Get Google Access Token Only Source: https://context7.com/adonisjs/ally/llms.txt Exchange an authorization code for an access token without fetching the user profile. This is useful when only the token is needed for subsequent API calls. The returned token object contains details like the access token, type, expiration, and refresh token. ```typescript import router from '@adonisjs/core/services/router' router.get('/google/callback', async ({ ally, response }) => { const google = ally.use('google') if (google.hasError()) { return response.badRequest(google.getError()) } // Get access token only (no user profile fetch) const token = await google.accessToken() // GoogleToken structure: // { // token: 'ya29.xxx', // type: 'Bearer', // expiresIn: 3599, // expiresAt: DateTime, // refreshToken: '1//xxx', // idToken: 'eyJhbGci...', // grantedScopes: ['email', 'profile'], // } // Use token for Google API calls const calendarResponse = await fetch( 'https://www.googleapis.com/calendar/v3/calendars/primary/events', { headers: { Authorization: `Bearer ${token.token}`, }, } ) return { accessToken: token.token, expiresAt: token.expiresAt?.toISO(), refreshToken: token.refreshToken, events: await calendarResponse.json(), } }) ``` -------------------------------- ### AllyManager Helper Methods Source: https://context7.com/adonisjs/ally/llms.txt Provides utility methods for checking provider configuration, capabilities, and managing dynamic provider selection. ```APIDOC ## GET /auth/providers ### Description Retrieves information about configured OAuth providers, including those that allow local signups. ### Method GET ### Endpoint /auth/providers ### Response #### Success Response (200) - **all** (array) - An array of all configured provider names. - **signup** (array) - An array of provider names that allow local signups. - **github** (object) - Information about the GitHub provider's configuration. - **configured** (boolean) - Whether the GitHub provider is configured. - **allowsSignup** (boolean) - Whether local signup is allowed with GitHub. #### Response Example ```json { "all": ["github", "google", "discord"], "signup": ["google", "discord"], "github": { "configured": true, "allowsSignup": true } } ``` ## GET /auth/:provider/redirect ### Description Initiates the OAuth redirect flow for a specified provider after validating its configuration and signup permissions. ### Method GET ### Endpoint /auth/:provider/redirect ### Parameters #### Path Parameters - **provider** (string) - Required - The name of the OAuth provider (e.g., 'github', 'google'). ### Response #### Success Response (200) - Redirects the user to the OAuth provider's authorization URL. #### Error Response (404 Not Found) - **message** (string) - Indicates that the specified provider is unknown. #### Error Response (403 Forbidden) - **message** (string) - Indicates that local signup is not allowed with the specified provider. ``` -------------------------------- ### Initiate OAuth Redirect with GitHub Source: https://context7.com/adonisjs/ally/llms.txt Use this to redirect users to the GitHub OAuth provider for authentication. Ensure the 'github' driver is configured in your ally.ts config file. ```typescript ally.use('github').redirect() ``` -------------------------------- ### Redirect to OAuth Provider with driver.redirect() Source: https://context7.com/adonisjs/ally/llms.txt Initiates the OAuth flow by redirecting the user to the provider's authorization page. Supports custom scopes, parameters, and stateless authentication. ```typescript import router from '@adonisjs/core/services/router' // Basic redirect router.get('/github/redirect', async ({ ally }) => { return ally.use('github').redirect() }) ``` ```typescript // Redirect with custom scopes router.get('/github/redirect/full', async ({ ally }) => { return ally.use('github').redirect((request) => { // Replace default scopes request.scopes(['user', 'user:email', 'read:org', 'repo']) // Add custom parameters request.param('allow_signup', 'true') request.param('login', 'octocat') }) }) ``` ```typescript // Google redirect with advanced options router.get('/google/redirect', async ({ ally }) => { return ally.use('google').redirect((request) => { request.scopes(['openid', 'userinfo.email', 'userinfo.profile', 'calendar.readonly']) request.param('access_type', 'offline') // Get refresh token request.param('prompt', 'consent') // Force consent screen request.param('hd', 'example.com') // Restrict to domain }) }) ``` ```typescript // Merge additional scopes with defaults router.get('/discord/redirect', async ({ ally }) => { return ally.use('discord').redirect((request) => { request.mergeScopes(['guilds', 'guilds.join']) }) }) ``` ```typescript // Stateless redirect (no CSRF cookie verification) router.get('/api/github/redirect', async ({ ally }) => { return ally.use('github').stateless().redirect() }) ``` -------------------------------- ### Handle GitHub OAuth Callback and User Profile Source: https://context7.com/adonisjs/ally/llms.txt Processes the OAuth callback from GitHub, exchanges the code for an access token, fetches the user profile, and handles user creation or update in the database before logging the user in. Ensure necessary imports and configurations for Ally and Auth are in place. ```typescript import router from '@adonisjs/core/services/router' import User from '#models/user' import hash from '@adonisjs/core/services/hash' router.get('/github/callback', async ({ ally, auth, response }) => { const github = ally.use('github') // Handle OAuth errors if (github.accessDenied()) { return response.redirect('/login?error=access_denied') } if (github.stateMisMatch()) { return response.redirect('/login?error=state_mismatch') } if (github.hasError()) { return response.redirect(`/login?error=${github.getError()}`) } // Get user profile from GitHub const githubUser = await github.user() // User object structure: // { // id: '12345', // Provider's unique user ID // nickName: 'octocat', // Username/handle // name: 'The Octocat', // Display name // email: 'octocat@github.com', // Email (may be null) // emailVerificationState: 'verified', // 'verified' | 'unverified' | 'unsupported' // avatarUrl: 'https://avatars.githubusercontent.com/u/12345', // token: { // token: 'gho_xxxx', // Access token // type: 'bearer', // Token type // scope: 'user:email', // Granted scopes // }, // original: { ... } // Raw provider response // } // Find or create user in database let user = await User.findBy('email', githubUser.email) if (!user) { user = await User.create({ email: githubUser.email, name: githubUser.name, avatarUrl: githubUser.avatarUrl, password: await hash.make(crypto.randomUUID()), githubId: githubUser.id, githubToken: githubUser.token.token, }) } else { // Update existing user's GitHub token user.githubId = githubUser.id user.githubToken = githubUser.token.token await user.save() } // Log in the user await auth.use('web').login(user) return response.redirect('/dashboard') }) ``` -------------------------------- ### Configure Social Authentication Providers with defineConfig Source: https://context7.com/adonisjs/ally/llms.txt Configure social authentication providers using the `defineConfig` function and built-in `services` helpers. Each service accepts provider-specific configuration including OAuth credentials and callback URLs. Ensure environment variables are set for client IDs and secrets. ```typescript import env from '#start/env' import { defineConfig, services } from '@adonisjs/ally' const allyConfig = defineConfig({ github: services.github({ clientId: env.get('GITHUB_CLIENT_ID'), clientSecret: env.get('GITHUB_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/github/callback', scopes: ['user', 'user:email'], allowSignup: true, }), google: services.google({ clientId: env.get('GOOGLE_CLIENT_ID'), clientSecret: env.get('GOOGLE_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/google/callback', scopes: ['openid', 'userinfo.email', 'userinfo.profile'], prompt: 'select_account', accessType: 'offline', }), discord: services.discord({ clientId: env.get('DISCORD_CLIENT_ID'), clientSecret: env.get('DISCORD_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/discord/callback', scopes: ['identify', 'email'], }), twitter: services.twitter({ clientId: env.get('TWITTER_API_KEY'), clientSecret: env.get('TWITTER_APP_SECRET'), callbackUrl: 'http://localhost:3333/twitter/callback', }), twitterX: services.twitterX({ clientId: env.get('TWITTER_X_CLIENT_ID'), clientSecret: env.get('TWITTER_X_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/twitter-x/callback', scopes: ['tweet.read', 'users.read', 'offline.access'], }), facebook: services.facebook({ clientId: env.get('FACEBOOK_CLIENT_ID'), clientSecret: env.get('FACEBOOK_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/facebook/callback', scopes: ['email', 'public_profile'], }), spotify: services.spotify({ clientId: env.get('SPOTIFY_CLIENT_ID'), clientSecret: env.get('SPOTIFY_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/spotify/callback', scopes: ['user-read-email', 'user-read-private'], }), linkedin: services.linkedin({ clientId: env.get('LINKEDIN_CLIENT_ID'), clientSecret: env.get('LINKEDIN_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/linkedin/callback', scopes: ['r_emailaddress', 'r_liteprofile'], }), linkedinOpenidConnect: services.linkedinOpenidConnect({ clientId: env.get('LINKEDIN_CLIENT_ID'), clientSecret: env.get('LINKEDIN_CLIENT_SECRET'), callbackUrl: 'http://localhost:3333/linkedin/callback', scopes: ['openid', 'profile', 'email'], }), }) // TypeScript type inference for configured providers declare module '@adonisjs/ally/types' { interface SocialProviders extends InferSocialProviders {} } export default allyConfig ``` -------------------------------- ### Fetch User Profile from GitHub Source: https://context7.com/adonisjs/ally/llms.txt After successful OAuth, use this to fetch the authenticated user's profile from GitHub. This method returns a standardized user object. ```typescript await ally.use('github').user() ``` -------------------------------- ### Handle OAuth Errors in GitHub Callback Source: https://context7.com/adonisjs/ally/llms.txt Check for common OAuth errors like access denied, state mismatch, and provider errors. Redirects the user to login with appropriate flash messages. ```typescript import router from '@adonisjs/core/services/router' import * as errors from '@adonisjs/ally/errors' router.get('/github/callback', async ({ ally, response, session }) => { const github = ally.use('github') try { // Check for common OAuth errors if (github.accessDenied()) { // User clicked "Deny" on the OAuth consent screen session.flash('error', 'You denied access to your GitHub account') return response.redirect('/login') } if (github.stateMisMatch()) { // CSRF protection triggered - state cookie doesn't match session.flash('error', 'Session expired. Please try again') return response.redirect('/login') } if (github.hasError()) { // Provider returned an error const errorCode = github.getError() session.flash('error', `GitHub error: ${errorCode}`) return response.redirect('/login') } const user = await github.user() return user } catch (error) { // Handle specific Ally errors if (error instanceof errors.E_OAUTH_MISSING_CODE) { // Authorization code missing from callback return response.badRequest('Missing authorization code') } if (error instanceof errors.E_OAUTH_STATE_MISMATCH) { // State validation failed return response.badRequest('State mismatch - possible CSRF attack') } if (error instanceof errors.E_UNKNOWN_ALLY_PROVIDER) { // Provider not configured return response.notFound('Provider not found') } if (error instanceof errors.E_LOCAL_SIGNUP_DISALLOWED) { // Signup attempted with disallowed provider return response.forbidden('Signup not allowed with this provider') } // Re-throw unknown errors throw error } }) ``` -------------------------------- ### driver.redirect() - Redirect to OAuth Provider Source: https://context7.com/adonisjs/ally/llms.txt Redirects the user to the OAuth provider's authorization page with automatic CSRF state management. Supports customizing scopes and additional parameters through an optional callback. ```APIDOC ## GET /github/redirect ### Description Performs a basic redirect to the GitHub OAuth provider. ### Method GET ### Endpoint /github/redirect ### Request Example ```typescript router.get('/github/redirect', async ({ ally }) => { return ally.use('github').redirect() }) ``` ## GET /github/redirect/full ### Description Redirects to the GitHub OAuth provider with custom scopes and additional parameters. ### Method GET ### Endpoint /github/redirect/full ### Request Example ```typescript router.get('/github/redirect/full', async ({ ally }) => { return ally.use('github').redirect((request) => { request.scopes(['user', 'user:email', 'read:org', 'repo']) request.param('allow_signup', 'true') request.param('login', 'octocat') }) }) ``` ## GET /google/redirect ### Description Redirects to the Google OAuth provider with advanced options including specific scopes, access type, prompt, and domain restriction. ### Method GET ### Endpoint /google/redirect ### Request Example ```typescript router.get('/google/redirect', async ({ ally }) => { return ally.use('google').redirect((request) => { request.scopes(['openid', 'userinfo.email', 'userinfo.profile', 'calendar.readonly']) request.param('access_type', 'offline') request.param('prompt', 'consent') request.param('hd', 'example.com') }) }) ``` ## GET /discord/redirect ### Description Redirects to the Discord OAuth provider, merging additional scopes with the default ones. ### Method GET ### Endpoint /discord/redirect ### Request Example ```typescript router.get('/discord/redirect', async ({ ally }) => { return ally.use('discord').redirect((request) => { request.mergeScopes(['guilds', 'guilds.join']) }) }) ``` ## GET /api/github/redirect ### Description Performs a stateless redirect to the GitHub OAuth provider, skipping CSRF cookie verification. ### Method GET ### Endpoint /api/github/redirect ### Request Example ```typescript router.get('/api/github/redirect', async ({ ally }) => { return ally.use('github').stateless().redirect() }) ``` ``` -------------------------------- ### Extend Base Classes for Custom Drivers Source: https://context7.com/adonisjs/ally/llms.txt For unsupported OAuth providers, extend the base Oauth2Driver or Oauth1Driver classes to create a custom driver. This allows integration with any OAuth provider. ```typescript import { Oauth2Driver } from '@adonisjs/ally/drivers/oauth2' export class CustomDriver extends Oauth2Driver { protected authorizationEndpoint = 'https://example.com/oauth/authorize' protected tokenEndpoint = 'https://example.com/oauth/token' protected scopes: string[] = ['read'] public getProfile(accessToken: string): Promise { // Implementation to fetch user profile } } ``` -------------------------------- ### Register Custom Slack Driver in Ally Configuration Source: https://context7.com/adonisjs/ally/llms.txt Register your custom Slack driver within the AdonisJS Ally configuration file (config/ally.ts). This involves defining a provider function that instantiates your custom driver with the necessary configuration, including client ID, client secret, and callback URL. ```typescript import { configProvider } from '@adonisjs/core' import { defineConfig, services } from '@adonisjs/ally' export default defineConfig({ // Built-in providers github: services.github({ /* ... */ }), // Custom Slack provider slack: (ctx) => new SlackDriver(ctx, { clientId: process.env.SLACK_CLIENT_ID!, clientSecret: process.env.SLACK_CLIENT_SECRET!, callbackUrl: 'http://localhost:3333/slack/callback', scopes: ['identity.basic', 'identity.email', 'identity.avatar'], }), }) ``` -------------------------------- ### Define Custom Slack OAuth2 Driver Source: https://context7.com/adonisjs/ally/llms.txt Extend Oauth2Driver to implement a custom Slack OAuth2 provider. This includes defining token types, scopes, and configuration, as well as overriding methods for authorization endpoints and user info retrieval. ```typescript import { HttpContext } from '@adonisjs/core/http' import { Oauth2Driver } from '@adonisjs/ally' import type { AllyUserContract, ApiRequestContract, Oauth2AccessToken, RedirectRequestContract } from '@adonisjs/ally/types' // Define your driver's token shape type SlackToken = Oauth2AccessToken & { teamId: string teamName: string } // Define available scopes type SlackScopes = 'identity.basic' | 'identity.email' | 'identity.avatar' | 'identity.team' // Define configuration interface interface SlackDriverConfig { clientId: string clientSecret: string callbackUrl: string scopes?: SlackScopes[] } export class SlackDriver extends Oauth2Driver { // OAuth endpoints protected accessTokenUrl = 'https://slack.com/api/oauth.v2.access' protected authorizeUrl = 'https://slack.com/oauth/v2/authorize' protected userInfoUrl = 'https://slack.com/api/users.identity' // Parameter names protected codeParamName = 'code' protected errorParamName = 'error' protected stateCookieName = 'slack_oauth_state' protected stateParamName = 'state' protected scopeParamName = 'user_scope' // Slack uses 'user_scope' for user tokens protected scopesSeparator = ',' constructor(ctx: HttpContext, public config: SlackDriverConfig) { super(ctx, config) this.loadState() // Required for state management } // Configure default scopes and parameters protected configureRedirectRequest(request: RedirectRequestContract) { request.scopes(this.config.scopes || ['identity.basic', 'identity.email']) } // Create authenticated API request protected getAuthenticatedRequest(url: string, token: string) { const request = this.httpClient(url) request.header('Authorization', `Bearer ${token}`) request.header('Accept', 'application/json') request.parseAs('json') return request } // Fetch user profile from Slack protected async getUserInfo(token: string, callback?: (request: ApiRequestContract) => void) { const request = this.getAuthenticatedRequest(this.userInfoUrl, token) if (callback) callback(request) const response = await request.get() if (!response.ok) { throw new Error(`Slack API error: ${response.error}`) } return { id: response.user.id, nickName: response.user.name, name: response.user.name, email: response.user.email, emailVerificationState: 'unsupported' as const, avatarUrl: response.user.image_512, original: response, } } // Check for access denied error accessDenied(): boolean { return this.getError() === 'access_denied' } // Get authenticated user async user(callback?: (request: ApiRequestContract) => void) { const token = await this.accessToken() const user = await this.getUserInfo(token.token, callback) return { ...user, token } } // Get user from existing token async userFromToken(token: string, callback?: (request: ApiRequestContract) => void) { const user = await this.getUserInfo(token, callback) return { ...user, token: { token, type: 'bearer' as const } } } } ``` -------------------------------- ### Refresh User Profile from Stored GitHub Token Source: https://context7.com/adonisjs/ally/llms.txt Fetches and updates the user's profile information using an existing GitHub access token stored in the user model. This is useful for refreshing data without requiring re-authentication. Ensure the user model has a `githubToken` field. ```typescript import router from '@adonisjs/core/services/router' // Verify and refresh user data from stored token router.get('/api/user/refresh', async ({ ally, auth }) => { const user = auth.user! if (!user.githubToken) { return { error: 'No GitHub token stored' } } try { // Fetch current GitHub profile using stored token const githubUser = await ally.use('github').userFromToken(user.githubToken) // Update user's profile with latest data user.name = githubUser.name user.avatarUrl = githubUser.avatarUrl await user.save() return { id: githubUser.id, name: githubUser.name, email: githubUser.email, avatarUrl: githubUser.avatarUrl, } } catch (error) { // Token may be expired or revoked return { error: 'Failed to fetch user profile', details: error.message } } }) ``` -------------------------------- ### Fetch Twitter User from OAuth1 Token and Secret Source: https://context7.com/adonisjs/ally/llms.txt Use this method for OAuth1 providers like Twitter to retrieve user profile information using an existing access token and secret. Ensure the token and secret are stored securely. ```typescript import router from '@adonisjs/core/services/router' // Verify Twitter OAuth1 credentials router.post('/api/auth/twitter/verify', async ({ ally, request }) => { const { token, secret } = request.only(['token', 'secret']) try { const twitterUser = await ally.use('twitter').userFromTokenAndSecret(token, secret) return { valid: true, user: { id: twitterUser.id, screenName: twitterUser.nickName, name: twitterUser.name, avatarUrl: twitterUser.avatarUrl, }, } } catch (error) { return { valid: false, error: error.message } } }) ``` ```typescript // Refresh Twitter user data from stored credentials router.get('/api/user/twitter/refresh', async ({ ally, auth }) => { const user = auth.user! if (!user.twitterToken || !user.twitterSecret) { return { error: 'No Twitter credentials stored' } } const twitterUser = await ally .use('twitter') .userFromTokenAndSecret(user.twitterToken, user.twitterSecret) user.name = twitterUser.name user.avatarUrl = twitterUser.avatarUrl await user.save() return { success: true, user: twitterUser } }) ``` -------------------------------- ### Verify External OAuth Token from Mobile App Source: https://context7.com/adonisjs/ally/llms.txt Validates an external OAuth token provided by a client (e.g., a mobile app) against a specified provider. Returns the user's basic information if the token is valid. This endpoint is useful for authenticating users from external applications. ```typescript import router from '@adonisjs/core/services/router' // Verify external OAuth token from mobile app router.post('/api/auth/verify-token', async ({ ally, request }) => { const { provider, token } = request.only(['provider', 'token']) try { const user = await ally.use(provider).userFromToken(token) return { valid: true, user: { id: user.id, email: user.email, name: user.name, }, } } catch (error) { return { valid: false, error: error.message } } }) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.