AOAuth2Application
Overview #
AOAuth2Application is the base class for creating applications that use OAuth2 authentication. It handles the OAuth2 authorization flow, token management, and token refresh automatically.
Purpose:
- Implement OAuth2 authentication flow
- Manage access and refresh tokens
- Handle token expiration and refresh
- Provide authorized API requests
Location: orchesty-nodejs-sdk/lib/Authorization/Type/OAuth2/AOAuth2Application.ts
When to Use #
Use AOAuth2Application for services that authenticate with OAuth2:
- Google APIs
- Facebook API
- GitHub API
- Salesforce
- Any API using OAuth 2.0 authorization
Don't use for static credentials - use ABasicApplication instead.
Class Hierarchy #
AApplication
↓
AOAuth2Application (You extend this)
↓
YourOAuth2Application (e.g., GoogleApplication, GitHubApplication)
Constructor #
constructor(protected provider: OAuth2Provider)
Parameters:
| Parameter | Type | Description |
|---|---|---|
provider | OAuth2Provider | OAuth2 provider service (injected from DI container) |
Example:
import { OAuth2Provider } from '../../lib/Authorization/Provider/OAuth2/OAuth2Provider';
import AOAuth2Application from '../../lib/Authorization/Type/OAuth2/AOAuth2Application';
export default class GoogleApplication extends AOAuth2Application {
// Provider is passed to parent constructor
}
// When registering:
const oauth2Provider = container.get(OAuth2Provider);
const googleApp = new GoogleApplication(oauth2Provider);
Abstract Methods You Must Implement #
getName() #
public abstract getName(): string
Returns the unique application identifier.
getPublicName() #
public abstract getPublicName(): string
Returns the display name.
getDescription() #
public abstract getDescription(): string
Returns the application description.
getAuthUrl() #
public abstract getAuthUrl(): string
Returns the OAuth2 authorization URL.
Example:
public getAuthUrl(): string {
return 'https://accounts.google.com/o/oauth2/v2/auth';
}
getTokenUrl() #
public abstract getTokenUrl(): string
Returns the OAuth2 token exchange URL.
Example:
public getTokenUrl(): string {
return 'https://oauth2.googleapis.com/token';
}
getScopes() #
public abstract getScopes(applicationInstall: ApplicationInstall): string[]
Returns the OAuth2 scopes to request.
Parameters:
| Parameter | Type | Description |
|---|---|---|
applicationInstall | ApplicationInstall | User installation |
Returns: string[] - Array of scope strings
Example:
public getScopes(applicationInstall: ApplicationInstall): string[] {
return [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/drive.readonly'
];
}
getFormStack() #
public abstract getFormStack(): FormStack
Defines the OAuth2 configuration form (client ID, client secret).
Example:
import CoreFormsEnum from '../../lib/Application/Base/CoreFormsEnum';
import Field from '../../lib/Application/Model/Form/Field';
import FieldType from '../../lib/Application/Model/Form/FieldType';
import Form from '../../lib/Application/Model/Form/Form';
import FormStack from '../../lib/Application/Model/Form/FormStack';
import { CLIENT_ID, CLIENT_SECRET } from '../../lib/Authorization/Type/OAuth2/IOAuth2Application';
public getFormStack(): FormStack {
const clientIdField = new Field(FieldType.TEXT, CLIENT_ID, 'Client ID');
const clientSecretField = new Field(FieldType.PASSWORD, CLIENT_SECRET, 'Client Secret');
const form = new Form(CoreFormsEnum.AUTHORIZATION_FORM, 'Authorization');
form.addField(clientIdField);
form.addField(clientSecretField);
const formStack = new FormStack();
formStack.addForm(form);
return formStack;
}
getRequestDto() #
public abstract getRequestDto(
dto: ProcessDto,
applicationInstall: ApplicationInstall,
method: HttpMethods,
url?: string,
data?: unknown
): RequestDto | Promise<RequestDto>
Creates an authenticated request with the OAuth access token.
Example:
public getRequestDto(
dto: ProcessDto,
applicationInstall: ApplicationInstall,
method: HttpMethods,
url?: string,
data?: unknown
): RequestDto {
const accessToken = this.getAccessToken(applicationInstall);
const requestDto = new RequestDto(url ?? '', method, dto, data);
requestDto.setHeaders({
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
});
return requestDto;
}
Inherited Methods #
getAccessToken() #
public getAccessToken(applicationInstall: ApplicationInstall): string
Gets the current access token from settings.
Throws: Error if token doesn't exist
authorize() #
public authorize(applicationInstall: ApplicationInstall): string
Generates the OAuth2 authorization URL for user to visit.
setAuthorizationToken() #
public async setAuthorizationToken(
applicationInstall: ApplicationInstall,
token: Record<string, string>
): Promise<void>
Exchanges authorization code for access token.
refreshAuthorization() #
public async refreshAuthorization(applicationInstall: ApplicationInstall): Promise<ApplicationInstall>
Refreshes an expired access token using refresh token.
isAuthorized() #
public isAuthorized(applicationInstall: ApplicationInstall): boolean
Checks if application has a valid access token.
getScopesSeparator() #
protected getScopesSeparator(): string
Returns scope separator (default: comma). Override if API uses different separator.
Example:
import ScopeSeparatorEnum from '../../lib/Authorization/ScopeSeparatorEnum';
protected getScopesSeparator(): string {
return ScopeSeparatorEnum.SPACE; // For APIs that use space-separated scopes
}
Usage Examples #
Complete OAuth2 Application #
From: orchesty-nodejs-sdk/test/Application/TestOAuth2Application.ts
import CoreFormsEnum from '../../lib/Application/Base/CoreFormsEnum';
import { ApplicationInstall } from '../../lib/Application/Database/ApplicationInstall';
import Field from '../../lib/Application/Model/Form/Field';
import FieldType from '../../lib/Application/Model/Form/FieldType';
import Form from '../../lib/Application/Model/Form/Form';
import FormStack from '../../lib/Application/Model/Form/FormStack';
import ScopeSeparatorEnum from '../../lib/Authorization/ScopeSeparatorEnum';
import AOAuth2Application from '../../lib/Authorization/Type/OAuth2/AOAuth2Application';
import { CLIENT_ID, CLIENT_SECRET } from '../../lib/Authorization/Type/OAuth2/IOAuth2Application';
import RequestDto from '../../lib/Transport/Curl/RequestDto';
import { HttpMethods } from '../../lib/Transport/HttpMethods';
import ProcessDto from '../../lib/Utils/ProcessDto';
export default class TestOAuth2Application extends AOAuth2Application {
public getAuthUrl(): string {
return 'https://identity.idoklad.cz/server/connect/authorize';
}
public getDescription(): string {
return 'Test OAuth2 application';
}
public getName(): string {
return 'oauth2application';
}
public getPublicName(): string {
return 'Test OAuth2 Application';
}
public getRequestDto(
dto: ProcessDto,
applicationInstall: ApplicationInstall,
method: HttpMethods,
url?: string,
data?: unknown
): RequestDto {
const accessToken = this.getAccessToken(applicationInstall);
const requestDto = new RequestDto(url ?? '', method, dto, data);
requestDto.setHeaders({
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
});
return requestDto;
}
public getFormStack(): FormStack {
const clientIdField = new Field(FieldType.TEXT, CLIENT_ID, 'Client ID');
const clientSecretField = new Field(FieldType.PASSWORD, CLIENT_SECRET, 'Client Secret');
const form = new Form(CoreFormsEnum.AUTHORIZATION_FORM, 'Authorization');
form.addField(clientIdField);
form.addField(clientSecretField);
const formStack = new FormStack();
formStack.addForm(form);
return formStack;
}
public getTokenUrl(): string {
return 'https://identity.idoklad.cz/server/connect/token';
}
public getScopes(applicationInstall: ApplicationInstall): string[] {
return ['idoklad_api', 'offline_access'];
}
protected getScopesSeparator(): string {
return ScopeSeparatorEnum.SPACE;
}
}
Using OAuth2 in Connector #
export default class GoogleDriveConnector extends AConnector {
public async processAction(dto: ProcessDto): Promise<ProcessDto> {
const appInstall = await this.getApplicationInstallFromProcess(dto);
const app = this.getApplication<GoogleApplication>();
// Check if token is still valid
const expires = appInstall.getExpires();
if (expires && expires < new Date()) {
// Token expired - refresh it
const refreshedInstall = await app.refreshAuthorization(appInstall);
// Update in database
await this.getDbClient()
.getApplicationRepository()
.update(refreshedInstall);
}
// Make authorized request
const requestDto = app.getRequestDto(
dto,
appInstall,
HttpMethods.GET,
'https://www.googleapis.com/drive/v3/files'
);
const responseDto = await this.getSender().send(requestDto);
dto.setData(responseDto.getBody());
return dto;
}
}
OAuth2 Flow #
1. User Authorization #
User clicks "Connect"
↓
System calls authorize()
↓
User redirected to OAuth2 provider
↓
User grants permissions
↓
Provider redirects back with authorization code
2. Token Exchange #
System receives authorization code
↓
Calls setAuthorizationToken()
↓
Exchanges code for access token + refresh token
↓
Tokens stored in ApplicationInstall
3. Making Requests #
Connector needs to call API
↓
Gets ApplicationInstall
↓
Calls getRequestDto() with ApplicationInstall
↓
Access token added to Authorization header
↓
Request sent to API
4. Token Refresh (when expired) #
Access token expired
↓
Calls refreshAuthorization()
↓
Uses refresh token to get new access token
↓
New tokens stored in ApplicationInstall
Common Patterns #
Pattern 1: Custom Authorization Options #
protected getProviderCustomOptions(applicationInstall: ApplicationInstall): Record<string, unknown> {
return {
options: {
authorizationMethod: 'body', // or 'header'
bodyFormat: 'json', // or 'form'
},
// Additional provider-specific options
prompt: 'consent',
access_type: 'offline'
};
}
Pattern 2: Dynamic Scopes #
public getScopes(applicationInstall: ApplicationInstall): string[] {
const settings = applicationInstall.getNonEncryptedSettings();
const baseScopes = ['email', 'profile'];
// Add optional scopes based on configuration
if (settings['enable_drive']) {
baseScopes.push('https://www.googleapis.com/auth/drive');
}
if (settings['enable_calendar']) {
baseScopes.push('https://www.googleapis.com/auth/calendar');
}
return baseScopes;
}
Pattern 3: API Versioning #
public getRequestDto(
dto: ProcessDto,
applicationInstall: ApplicationInstall,
method: HttpMethods,
url?: string,
data?: unknown
): RequestDto {
const accessToken = this.getAccessToken(applicationInstall);
const settings = applicationInstall.getNonEncryptedSettings();
const apiVersion = settings['api_version'] || 'v1';
// Prepend version to URL if not already present
const fullUrl = url?.includes('://') ? url : `https://api.example.com/${apiVersion}${url}`;
const requestDto = new RequestDto(fullUrl, method, dto, data);
requestDto.setHeaders({
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
});
return requestDto;
}
Next Steps #
- ABasicApplication - For simple authentication
- FormStack - Build configuration forms
- ApplicationInstall - How tokens are stored
- DIContainer - Register your application
- OAuth2Provider - OAuth2 provider service