async function handleAuthCodeExchange()

in ReferenceAppKotlin/firebase/server/src/controller/functions/authentication.ts [367:429]


async function handleAuthCodeExchange(
    req: functions.Request, res: functions.Response) {
  const client_id = req.body.client_id;
  const code = req.body.code;
  const db = firebase.app().firestore();

  // Verify authorization code
  const codeSnapshot = await db.collection('authcodes')
      .orderBy('created_on', 'desc')
      .where('client_id', '==', client_id)
      .where('auth_code', '==', code)
      .get();
  if (codeSnapshot.empty) {
    res.status(400).send({
      error: 'invalid_grant',
      error_description: 'Invalid authorization code'
    });
    return;
  }

  // Pick the newest entry in the database, probably redundant (there should
  //     only be one entry anyway)
  const code_info = codeSnapshot.docs[0];
  const user_id = code_info.get('user_id');
  const created_on = code_info.get('created_on');
  const expires_in = code_info.get('expires_in');
  // Check if token is expired
  if (created_on + expires_in * 1000 < Date.now()) {
    res.status(400).send({
      error: 'invalid_grant',
      error_description: 'Authorization code expired'
    });
    return;
  }

  // Generate tokens
  const access_token = await generateToken(32, 'token');
  const refresh_token = await generateToken(64, 'token');
  try {
    // Store in database
    await db.collection('tokens').add({
      client_id: client_id,
      user_id: user_id,
      scope: [],
      refresh_token: refresh_token,
      access_token: access_token,
      expires_in: ACCESS_TOKEN_EXPIRY,
      created_on: Date.now()
    });

    // Return generated tokens if database write was successful
    res.status(200).send({
      access_token: access_token,
      expires_in: ACCESS_TOKEN_EXPIRY,
      refresh_token: refresh_token,
      token_type: 'Bearer'
    });
  } catch (err) {
    // Return error if database write was unsuccessful
    console.error(err);
    res.status(500).send(err);
  }
}