{"library":"oauth2-server","title":"OAuth2 Server for Node.js","description":"oauth2-server is a complete, framework-agnostic, and well-tested module for implementing an OAuth2 Authorization Server in Node.js. It adheres to RFC 6749 (OAuth 2.0 Authorization Framework) and RFC 6750 (Bearer Token Usage), providing the core logic for handling various OAuth 2.0 grant types including `authorization_code`, `client_credentials`, `refresh_token`, and `password` grants, as well as support for custom extension grants and scopes. The library is currently at version 3.1.1 and is under active maintenance, with recent releases focusing on bug fixes and dependency updates after a period of hiatus. It distinguishes itself by offering a robust, compliant foundation that can be integrated with any Node.js HTTP framework (like Express or Koa via official wrappers), supporting promises, Node-style callbacks, and async/await for model interactions. It doesn't dictate a specific storage mechanism, allowing developers to plug in their preferred database (e.g., PostgreSQL, MongoDB, Redis).","language":"javascript","status":"active","last_verified":"Tue Apr 21","install":{"commands":["npm install oauth2-server"],"cli":null},"imports":["import OAuth2Server from 'oauth2-server';","import { Request, Response } from 'oauth2-server';","import OAuth2Server from '@oauthjs/express-oauth-server';"],"auth":{"required":false,"env_vars":[]},"quickstart":{"code":"import express from 'express';\nimport OAuth2Server, { Request, Response } from 'oauth2-server';\nimport bodyParser from 'body-parser';\n\n// A minimalistic in-memory model for demonstration purposes.\nconst model = {\n  // Client storage\n  clients: [{\n    id: 'client1',\n    secret: 'clientsecret',\n    redirectUris: ['http://localhost:3000/oauth/callback'],\n    grants: ['authorization_code', 'refresh_token', 'password', 'client_credentials'],\n  }],\n  // Token/Code storage\n  tokens: [],\n  authorizationCodes: [],\n\n  // Required: getClient(clientId, clientSecret, callback)\n  async getClient(clientId, clientSecret) {\n    const client = this.clients.find(c => c.id === clientId);\n    if (!client) return null;\n    if (clientSecret && client.secret !== clientSecret) return null;\n    return client;\n  },\n\n  // Required for authorization_code grant\n  async saveAuthorizationCode(code, client, user) {\n    this.authorizationCodes.push({ code: code.authorizationCode, expiresAt: code.expiresAt, redirectUri: code.redirectUri, scope: code.scope, client: client, user: user });\n    return code;\n  },\n  async getAuthorizationCode(authorizationCode) {\n    const code = this.authorizationCodes.find(c => c.code === authorizationCode && c.expiresAt > new Date());\n    if (!code) return null;\n    // In a real app, delete the code after use: this.authorizationCodes = this.authorizationCodes.filter(c => c.code !== authorizationCode);\n    return code;\n  },\n  async revokeAuthorizationCode(code) {\n    this.authorizationCodes = this.authorizationCodes.filter(c => c.code !== code.authorizationCode);\n    return true;\n  },\n\n  // Required for password, client_credentials, refresh_token grants\n  async saveToken(token, client, user) {\n    this.tokens.push({ accessToken: token.accessToken, accessTokenExpiresAt: token.accessTokenExpiresAt, refreshToken: token.refreshToken, refreshTokenExpiresAt: token.refreshTokenExpiresAt, scope: token.scope, client: client, user: user });\n    return token;\n  },\n  async getAccessToken(accessToken) {\n    return this.tokens.find(t => t.accessToken === accessToken && t.accessTokenExpiresAt > new Date());\n  },\n  async getRefreshToken(refreshToken) {\n    return this.tokens.find(t => t.refreshToken === refreshToken && t.refreshTokenExpiresAt > new Date());\n  },\n  async revokeToken(token) {\n    this.tokens = this.tokens.filter(t => t.refreshToken !== token.refreshToken);\n    return true;\n  },\n\n  // Required for password grant\n  async getUser(username, password) {\n    // Dummy user for 'password' grant\n    if (username === 'testuser' && password === 'testpass') {\n      return { id: '123', username: 'testuser' };\n    }\n    return null;\n  },\n\n  // Optional: verifyScope(token, scope)\n  async verifyScope(token, scope) {\n    if (!scope) return true;\n    const requestedScopes = scope.split(' ');\n    const authorizedScopes = token.scope.split(' ');\n    return requestedScopes.every(s => authorizedScopes.includes(s));\n  }\n};\n\nconst app = express();\napp.use(bodyParser.json());\napp.use(bodyParser.urlencoded({ extended: false }));\n\nconst oauth = new OAuth2Server({\n  model: model,\n  accessTokenLifetime: 60 * 60, // 1 hour\n  allowBearerTokensInQueryString: true, // Only for testing, not recommended for production.\n});\n\n// Grant token endpoint (e.g., /oauth/token for password, client_credentials, refresh_token grants)\napp.post('/oauth/token', async (req, res) => {\n  try {\n    const token = await oauth.token(new Request(req), new Response(res));\n    res.json(token);\n  } catch (err) {\n    console.error(err);\n    res.status(err.code || 500).json(err);\n  }\n});\n\n// Authorize endpoint (e.g., /oauth/authorize for authorization_code grant)\napp.get('/oauth/authorize', async (req, res) => {\n  // In a real application, render an authorization screen here\n  // For this example, we'll auto-approve if 'user' query param is present\n  if (req.query.response_type === 'code' && req.query.client_id && req.query.redirect_uri) {\n    if (req.query.user === 'approved') {\n      const authRequest = new Request(req);\n      const authResponse = new Response(res);\n      try {\n        const code = await oauth.authorize(authRequest, authResponse, {\n          authenticateHandler: { handle: async () => ({ id: '123', username: 'testuser' }) }\n        });\n        res.redirect(code.redirectUri + '?code=' + code.authorizationCode);\n      } catch (err) {\n        console.error(err);\n        res.status(err.code || 500).json(err);\n      }\n    } else {\n      // Simulate a login/consent page redirect\n      res.send(`\n        <h1>Authorize Client</h1>\n        <p>Client ${req.query.client_id} wants access to your data.</p>\n        <form action=\"/oauth/authorize\" method=\"GET\">\n          <input type=\"hidden\" name=\"response_type\" value=\"${req.query.response_type}\">\n          <input type=\"hidden\" name=\"client_id\" value=\"${req.query.client_id}\">\n          <input type=\"hidden\" name=\"redirect_uri\" value=\"${req.query.redirect_uri}\">\n          <input type=\"hidden\" name=\"scope\" value=\"${req.query.scope || ''}\">\n          <button type=\"submit\" name=\"user\" value=\"approved\">Approve</button>\n        </form>\n      `);\n    }\n  } else {\n    res.status(400).json({ error: 'invalid_request', error_description: 'Missing required parameters for authorization.' });\n  }\n});\n\n// Protected resource endpoint\napp.get('/protected', async (req, res) => {\n  try {\n    const token = await oauth.authenticate(new Request(req), new Response(res));\n    res.json({ message: 'Hello, ' + token.user.username + '!', tokenInfo: token });\n  } catch (err) {\n    console.error(err);\n    res.status(err.code || 500).json(err);\n  }\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`OAuth2 server listening on port ${PORT}`);\n  console.log('Try POST to /oauth/token with grant_type=password, username=testuser, password=testpass, client_id=client1, client_secret=clientsecret');\n  console.log('Try GET to /oauth/authorize with ?response_type=code&client_id=client1&redirect_uri=http://localhost:3000/oauth/callback&scope=read');\n  console.log('Then navigate to /protected with Authorization: Bearer <access_token>');\n});\n","lang":"javascript","description":"This quickstart sets up a basic OAuth2 server using Express.js and `oauth2-server`, demonstrating `password`, `client_credentials`, `refresh_token`, and `authorization_code` grants with an in-memory data model. It includes token and authorization endpoints, and a protected resource. This minimal model is for illustration; a real application requires persistent storage.","tag":null,"tag_description":null,"last_tested":null,"results":[]},"compatibility":null}