Hapi and Bell's attempt at authenticating with Twitter was unsuccessful

Currently, I have developed a basic backend API that requires multiple authentications. My current challenge is connecting to the Twitter API using Bell. However, instead of displaying the authentication page for the app, an error is being shown: {"statusCode":500,"error":"Internal Server Error","message":"An internal server error occurred"}

Below are the dependency files:

index.ts

'use strict';

import * as hapi from "hapi";
import * as boom from "boom";
import router from './router/router';

const server: hapi.Server = new hapi.Server();
server.connection({ port: 3000, host: 'localhost' });

// .register(...) registers a module within the instance of the API. The callback is then used to tell that the loaded module will be used as an authentication strategy. 
server.register( [require( 'hapi-auth-jwt' ), require('hapi-auth-cookie'), require('bell')], ( err ) => {

    // normal server auth strategy using JWT
    server.auth.strategy( 'token', 'jwt', {  
        key: 'secretkey',
        verifyOptions: {
            algorithms: [ 'HS256' ],
        },
        redirectTo: '/login'
    } );

    //Setup the session strategy
    server.auth.strategy('session', 'cookie', {
        password: 'secret_cookie_encryption_password', //Use something more secure in production
        redirectTo: '/login', //If there is no session, redirect here
        isSecure: false //Should be set to true (which is the default) in production
    });

  //Setup the social Twitter login strategy
    server.auth.strategy('twitter', 'bell', {
        provider: 'twitter',
        password: 'secret_cookie_encryption_password', //Use something more secure in production
        clientId: 'secretkey',
        clientSecret: ' secretkey',
        isSecure: false //Should be set to true (which is the default) in production
    });

    //server.auth.default('token');

    // Make sure server get auth first before attach the router
    router.forEach( ( router ) => {
        console.log( `attaching ${ router.path }` );
        server.route( router );
    } );

} );



server.start((err) => {

    if (err) {
        throw err;
    }
    console.log(`Server running at: ${server.info.uri}`);
});

router.ts

'use strict';

import controllers from '../server/controllers/Index';
import models from '../server/models/index';
import { compareHashPassword } from '../Helper';
import * as jwt from "jsonwebtoken";
import * as Boom from "boom";

// Init express router saja
let router;
let User = models.User;

// Setting API URL
router = [
    {
        method: 'GET',
        path: '/',
        config: {
          auth: {
            strategies: ['token', 'session'],        
        }, //<-- require a session for this, so we have access to the twitter profile
          handler: function(request, reply) {

            //Return a message using the information from the session
            return reply('Hello, ' + request.auth.credentials.displayName + '!');
          }
        }
    },
    {
        method: 'GET',
        path: '/login',
        handler: function(request, reply) {
            return reply('Please Login to ReviewUr!');
        }
    },
    // Authentication route for Token
    {
        path: '/auth',
        method: 'POST',
        handler: controllers.Auths.list
    },
    // Authentication route for Twitter
    {
        method: 'GET',
        path: '/auth/twitter',
        config: {
          auth: 'twitter',
          handler: function(request, reply) {

            if (!request.auth.isAuthenticated) {
              //return reply(Boom.unauthorized('Authentication failed: ' + request.auth.error.message));
                return reply('unauthorized!');
            }


            const profile = request.auth.credentials.profile;

            request.cookieAuth.set({
              twitterId: profile.id,
              username: profile.username,
              displayName: profile.displayName
            });

            return reply.redirect('/').catch(error => reply(error));
          }
        }
    },
   ///////////////////////////////////////
];

export default router

Do you think there might be something important that I'm overlooking? Let me know your thoughts.

Answer №1

Have you attempted moving the '/auth/twitter' route above '/auth' to ensure it handles '/auth' prior to '/auth/twitter'? Where exactly are you aiming to display the user sign-in options - perhaps in the /login route?

What about trying without jwt or auth-cookie, only utilizing bell with Twitter? It could be that there's a conflict with other plugins.

This implementation I'm sharing has been extracted from an active application:

app-auth.js

const Boom = require('boom');
const internals = {};
const config = require('config');

exports.register = function (server, options, next) {
    server.auth.scheme('basic', internals.implementation);
    server.auth.strategy('simple', 'basic', true);

    // handle twitter login errors here
    server.ext('onPreResponse', function (request, reply) {
        const req = request.response;
        if (req.isBoom && request.path === 'login') {
            // encountering an error during the login process will redirect the user to the home page
            server.log(['error', 'auth', 'twitter'], req.output.payload.error);
            return reply.redirect('/?error=103&account_error=' + encodeURIComponent(req.output.payload.error));
        }

        return reply.continue();
    });

    // registering twitter application
    server.auth.strategy('twitter', 'bell', {
        provider: 'twitter',
        password: config.get('twitter.pass'),
        clientId: config.get('twitter.key'),
        clientSecret: config.get('twitter.secret'),
        isSecure: config.get('authSecurity')
    });

    return next();
};

internals.implementation = function (server, options) {

    return {
        authenticate: function (request, reply) {
            // in the absence of session information
            if (!request.yar.get('auth')) {
                const headers = request.headers;
                // if this request is for xmlhttp, respond with json
                if (headers['x-requested-with'] === 'XMLHttpRequest') {
                    return reply(Boom.unauthorized("Please sign-in to system."));
                }
                return reply.redirect('/login');
            }
            return reply.continue({credentials: request.yar.get('auth')});
        }
    }
};

exports.register.attributes = {
    name: 'app_auth',    
    version: require('../package.json').version
};

twitter-route.js

exports.view = {
    description: 'Twitter authentication handler',
    auth: 'twitter',
    handler: async (request, reply) => {
        // encountering an issue
        if (!request.auth.isAuthenticated) {
            request.yar.flash('ERROR', request.auth.error.message);
            return reply.redirect('/login_error?error=103&account_error=' + encodeURIComponent(request.auth.error.message));
        }

        // accessing profile data from twitter
        const profile = request.auth.credentials.profile;

        // perform actions based on the profile

    }
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Discovering the object and its parent within a series of nested arrays

Is there a way to locate an object and its parent object within a nested array of unknown size using either lodash or native JavaScript? The structure of the array could resemble something like this: name: 'Submodule122'</p> I have been ...

TypeScript does not perform type checking on arrays created using the Array() constructor and filled with the fill method

Using TypeScript version 2.4.2, compiled with the --target ES6 option has interesting results. For example, when using this line of code: var coins: { coin: number}[] = [1,1,1] TypeScript throws an error: Error TS2322: Type 'number[]' is no ...

How can you utilize both defineProps with TypeScript and set default values in Vue 3 setup? (Typescript)

Is there a way to use TypeScript types and default values in the "defineProps" function? I'm having difficulty getting it to work. Code snippet: const props = defineProps<{ type?: string color?: 'color-primary' | 'color-danger&a ...

The Ionic 5 app features a white iframe that functions perfectly on the web platform

Whenever I run my web application's function, the iframe is displayed. However, on Android, all I see is a white screen. Can anyone assist with resolving this issue? HMTL html <ion-content> <ion-button expand="full" color="warning" (clic ...

How can I prevent the installation of my Ionic 2 application on devices that have been rooted or jailbroken?

I am currently working on a project involving an Ionic 2 and Angular 2 application. I need to implement a feature that checks whether the device is rooted (in the case of Android) or jailbroken (in the case of iOS). I have experimented with various packag ...

What's preventing me from using just one comparison condition in TypeScript?

The issue at hand is quite simple: An error occurred because I tried to compare a number with a 'Ref<number>' object. It seems ridiculous that I can't compare two numbers, but as I am new to Typescript, I would greatly appreciate some ...

A step-by-step guide on customizing the background color of a Dialog in Angular Material (Version 16)

I've been attempting to modify the background color of my Angular Material Dialog by utilizing the panelClass property in the MatDialogConfig. Unfortunately, I'm encountering a partial success. I am aiming to set the background color as red (jus ...

Encountering an error of TypeError while attempting to generate a new GraphQL

Currently using Apollo-Server/TypeScript with graphql-tool's makeExecutableSchema() to set up schema/directives. Encountering an error while attempting to add a basic GraphQL Directive: TypeError: Class constructor SchemaDirectiveVisitor cannot be in ...

Error TS2339: The 'selectpicker' property is not found on the 'JQuery<HTMLElement>' type

Recently, I integrated the amazing bootstrap-select Successfully imported bootstrap-select into my project with the following: <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstra ...

What is the best way to specify the return type in TypeScript when there is a possibility of multiple types?

I'm currently working on implementing type definitions for an Axios API client but I’m struggling with indicating to the compiler the specific return type that I am expecting. Here is a simplified example: import axios, { AxiosResponse } from "axi ...

Patiently waiting for the component variable to be assigned through subscription

I am facing an issue with two calls in my component. The second call depends on the result from the first call. In the first call, I set the value for my component variable "locked". The second call should only be executed when the result is true, meaning ...

Exploring the Power of Vercel Deployment: Crafting a Custom CORS Middleware for Your API

Recently, I have been testing different methods to avoid a CORS error in my upcoming app deployed on Vercel. The only solution that worked for me was manually setting the headers for each API request, as shown below: export default async function handler( ...

Using the spread operator in the console.log function is successful, but encountering issues when attempting to assign or return it in a

Currently facing an issue with a spread operator that's really getting on my nerves. Despite searching extensively, I haven't found a solution yet. Whenever I utilize console.log(...val), it displays the data flawlessly without any errors. Howev ...

Is it possible to manipulate an Object within Object typescript?

My recent project involved working with React and Typescript to fetch data from an API. Once the data is fetched, it is saved as an object called coin. However, I encountered a situation where the data may not be fully loaded, resulting in coin being null. ...

Error: A variable is potentially 'undefined' (ts2532), even though I have just assigned a value to it

Encountering Object is possibly 'undefined'. ts(2532) errors in TypeScript for an optional parameter, despite being clearly defined... interface Foo { keyValue?: Record<string, string> } const a: Foo = { keyValue: { key1: 'value&apo ...

Invoking a functionality within a stream of events through an observable's subscribe

Service2.ts public flags$: BehaviorSubject<FlagName> = new BehaviorSubject<FlagName>("custom-flag-1"); This flag is set up as follows: private _setFlags = () => { const flagsData = this._customClient.getFlags(); if (f ...

Understanding the correct way to map two arrays with boolean values is essential for effective data

My situation involves two lists: accounts and accountsWithSelectedField. I initially mapped accountsWithSelectedField like this: this.accountsWithSelectedField = this.accounts.map(s => ({...s, selected: false})); Subsequently, upon receiving a list of ...

An error occurred: Unable to locate the file or assembly 'Interop.iTunesLib, Version=1.13.0.0, Culture=neutral, PublicKeyToken=null'

I've been attempting to connect to iTunes using C# programming language. The process involves creating a dll in C# and running it with TypeScript through the Overwolf API. Here's what I've done so far: Generated a .dll file I utilized the ...

Is it possible to create my TypeORM entities in TypeScript even though my application is written in JavaScript?

While I find it easier to write typeorm entities in TypeScript format, my entire application is written in JavaScript. Even though both languages compile the same way, I'm wondering if this mixed approach could potentially lead to any issues. Thank yo ...

The function Event.target.value is coming back as null

I've been working on creating a timer window, and I've set up a separate component for it. However, I'm facing an issue with passing the time from the main component to the child component. The problem lies in the fact that the state of the ...