or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-token.mdauthorization-code.mdclient-credentials.mdindex.mdresource-owner-password.md

resource-owner-password.mddocs/

0

# Resource Owner Password Grant

1

2

The Resource Owner Password Credentials Grant allows applications to directly use a user's credentials (username and password) to obtain an access token. This flow is only recommended for trusted applications where other flows are not viable.

3

4

## Overview

5

6

This grant type implements the OAuth 2.0 Resource Owner Password Credentials flow, which directly exchanges user credentials for an access token. **This flow is deprecated in OAuth 2.0 Security Best Current Practice** due to security concerns, but is still supported for legacy applications.

7

8

**Warning:** Only use this grant type for highly trusted applications (like official mobile apps) where other OAuth flows are not possible.

9

10

## Core Import

11

12

```javascript

13

const { ResourceOwnerPassword } = require('simple-oauth2');

14

```

15

16

## Resource Owner Password Class

17

18

### Constructor

19

20

```typescript { .api }

21

constructor(options: OAuth2Config): ResourceOwnerPassword

22

```

23

24

Creates a new ResourceOwnerPassword instance with OAuth 2.0 configuration validation.

25

26

**Parameters:**

27

- `options` - OAuth 2.0 configuration object (see main documentation)

28

29

**Example:**

30

```javascript

31

const client = new ResourceOwnerPassword({

32

client: {

33

id: 'your-client-id',

34

secret: 'your-client-secret'

35

},

36

auth: {

37

tokenHost: 'https://oauth-provider.com',

38

tokenPath: '/oauth/token'

39

}

40

});

41

```

42

43

### Get Access Token

44

45

```typescript { .api }

46

getToken(params: PasswordParams, httpOptions?: any): Promise<AccessToken>

47

```

48

49

Requests an access token using user credentials.

50

51

**Parameters:**

52

- `params.username` (string) - User's username or email

53

- `params.password` (string) - User's password

54

- `params.scope` (string | string[], optional) - Application scopes to request

55

- Additional parameters are automatically serialized for the token request

56

- `httpOptions` (object, optional) - HTTP options passed to underlying request library

57

58

**Returns:** Promise resolving to AccessToken instance

59

60

**Example:**

61

```javascript

62

// Basic authentication

63

const accessToken = await client.getToken({

64

username: 'user@example.com',

65

password: 'user-password'

66

});

67

68

// Authentication with specific scopes

69

const accessToken = await client.getToken({

70

username: 'user@example.com',

71

password: 'user-password',

72

scope: ['read', 'write', 'profile']

73

});

74

75

// Authentication with custom HTTP options

76

const accessToken = await client.getToken({

77

username: 'user@example.com',

78

password: 'user-password',

79

scope: 'api:access'

80

}, {

81

timeout: 15000,

82

headers: {

83

'User-Agent': 'TrustedApp/2.0'

84

}

85

});

86

87

console.log('Access token:', accessToken.token.access_token);

88

console.log('Refresh token:', accessToken.token.refresh_token);

89

```

90

91

### Create Token from Object

92

93

```typescript { .api }

94

createToken(token: any): AccessToken

95

```

96

97

Creates an AccessToken instance from a plain token object (e.g., from storage).

98

99

**Parameters:**

100

- `token` - Plain object representing an access token conforming to RFC 6750

101

102

**Returns:** AccessToken instance with full token management capabilities

103

104

**Example:**

105

```javascript

106

// Restore token from secure storage

107

const storedToken = await secureStorage.getToken(userId);

108

const accessToken = client.createToken(storedToken);

109

110

// Refresh token if expired

111

if (accessToken.expired()) {

112

const refreshedToken = await accessToken.refresh();

113

await secureStorage.saveToken(userId, refreshedToken.token);

114

}

115

```

116

117

## Type Definitions

118

119

```typescript { .api }

120

interface PasswordParams {

121

username: string;

122

password: string;

123

scope?: string | string[];

124

[key: string]: any;

125

}

126

```

127

128

## Common Usage Patterns

129

130

### Mobile App Authentication

131

132

```javascript

133

const { ResourceOwnerPassword } = require('simple-oauth2');

134

135

// Only use in trusted mobile applications

136

const client = new ResourceOwnerPassword({

137

client: {

138

id: process.env.MOBILE_APP_CLIENT_ID,

139

secret: process.env.MOBILE_APP_CLIENT_SECRET

140

},

141

auth: {

142

tokenHost: 'https://api.company.com',

143

tokenPath: '/oauth/token'

144

}

145

});

146

147

// Handle user login

148

async function authenticateUser(username, password) {

149

try {

150

const accessToken = await client.getToken({

151

username: username,

152

password: password,

153

scope: 'profile data:read data:write'

154

});

155

156

// Store token securely

157

await secureStorage.setItem('auth_token', JSON.stringify(accessToken.token));

158

159

return {

160

success: true,

161

token: accessToken.token

162

};

163

} catch (error) {

164

console.error('Authentication failed:', error.message);

165

return {

166

success: false,

167

error: error.message

168

};

169

}

170

}

171

```

172

173

### Legacy Application Migration

174

175

```javascript

176

// For migrating legacy applications to OAuth 2.0

177

class LegacyAuthAdapter {

178

constructor(oauthConfig) {

179

this.oauth = new ResourceOwnerPassword(oauthConfig);

180

this.tokenCache = new Map();

181

}

182

183

async login(username, password) {

184

try {

185

const accessToken = await this.oauth.getToken({

186

username,

187

password,

188

scope: 'legacy:api'

189

});

190

191

// Cache token for this user session

192

this.tokenCache.set(username, accessToken);

193

194

return accessToken.token;

195

} catch (error) {

196

throw new Error(`Login failed: ${error.message}`);

197

}

198

}

199

200

async getValidToken(username) {

201

const cachedToken = this.tokenCache.get(username);

202

203

if (!cachedToken) {

204

throw new Error('User not authenticated');

205

}

206

207

// Check if token needs refresh

208

if (cachedToken.expired(300)) { // 5 minute buffer

209

const refreshedToken = await cachedToken.refresh();

210

this.tokenCache.set(username, refreshedToken);

211

return refreshedToken.token;

212

}

213

214

return cachedToken.token;

215

}

216

217

async makeAuthenticatedRequest(username, url, options = {}) {

218

const token = await this.getValidToken(username);

219

220

return fetch(url, {

221

...options,

222

headers: {

223

...options.headers,

224

'Authorization': `Bearer ${token.access_token}`

225

}

226

});

227

}

228

}

229

230

// Usage

231

const authAdapter = new LegacyAuthAdapter({

232

client: {

233

id: 'legacy-app-id',

234

secret: process.env.LEGACY_APP_SECRET

235

},

236

auth: {

237

tokenHost: 'https://auth.company.com'

238

}

239

});

240

241

// User login

242

await authAdapter.login('user@company.com', 'password');

243

244

// Make authenticated API calls

245

const response = await authAdapter.makeAuthenticatedRequest(

246

'user@company.com',

247

'https://api.company.com/user/profile'

248

);

249

```

250

251

### Desktop Application with Token Persistence

252

253

```javascript

254

const path = require('path');

255

const fs = require('fs').promises;

256

const { ResourceOwnerPassword } = require('simple-oauth2');

257

258

class DesktopAuthClient {

259

constructor(clientConfig) {

260

this.oauth = new ResourceOwnerPassword(clientConfig);

261

this.tokenPath = path.join(process.env.HOME, '.myapp', 'token.json');

262

}

263

264

async authenticate(username, password) {

265

try {

266

const accessToken = await this.oauth.getToken({

267

username,

268

password,

269

scope: 'desktop:full'

270

});

271

272

// Save token to file

273

await this.saveToken(accessToken.token);

274

return accessToken;

275

} catch (error) {

276

throw new Error(`Authentication failed: ${error.message}`);

277

}

278

}

279

280

async getStoredToken() {

281

try {

282

const tokenData = await fs.readFile(this.tokenPath, 'utf8');

283

const token = JSON.parse(tokenData);

284

return this.oauth.createToken(token);

285

} catch (error) {

286

return null; // No stored token

287

}

288

}

289

290

async saveToken(token) {

291

const dir = path.dirname(this.tokenPath);

292

await fs.mkdir(dir, { recursive: true });

293

await fs.writeFile(this.tokenPath, JSON.stringify(token, null, 2));

294

}

295

296

async getValidToken() {

297

const storedToken = await this.getStoredToken();

298

299

if (!storedToken) {

300

throw new Error('No authentication token found. Please login first.');

301

}

302

303

if (storedToken.expired()) {

304

const refreshedToken = await storedToken.refresh();

305

await this.saveToken(refreshedToken.token);

306

return refreshedToken;

307

}

308

309

return storedToken;

310

}

311

}

312

313

// Usage

314

const authClient = new DesktopAuthClient({

315

client: {

316

id: 'desktop-app-id',

317

secret: process.env.DESKTOP_APP_SECRET

318

},

319

auth: {

320

tokenHost: 'https://auth.company.com'

321

}

322

});

323

324

// Initial authentication

325

await authClient.authenticate('user@company.com', 'password');

326

327

// Later, get valid token (will refresh if needed)

328

const token = await authClient.getValidToken();

329

```

330

331

## Security Considerations

332

333

When using the Resource Owner Password grant:

334

335

1. **Only use in trusted applications** - Never use in third-party applications

336

2. **Secure credential handling** - Never log or store user passwords

337

3. **Use HTTPS always** - Credentials are transmitted in requests

338

4. **Implement proper token storage** - Use secure storage mechanisms

339

5. **Consider migration path** - Plan to migrate to Authorization Code flow when possible

340

6. **Short token lifetimes** - Use shorter expiration times than other flows