Code Monkey home page Code Monkey logo

universal-social-auth's Introduction

Known Vulnerabilities

universal-social-auth

SPA universal social auth

Laravel

Php

Vue2

Vue3

Nuxt

React

Angularjs

More

if you wish you can buy me a coffee @ Patreon

universal-social-auth is easily configurable solution for frontend frameworks Vue2.js, Vue3.js, Nuxtjs, React, Angularjs, Otherjs Framework that support TypeScript & JavaScript & backend frameworks Laravel and Other Php Frameworks with Socialite that provides Social login using Github, Facebook, Google, Vkontakte and other OAuth providers can also work with Nodejs backend App.

NOTE: 10x faster then vue-social-auth and less file sizes

looking for old version vue-social-auth

WARNING: Default request library is Axios.

NOTE: It also works with any PHP with Socialite

NOTE: New features*

1: Ability to add none existing provider

2: Importation only Required provider

3: No more Buffer functions were called based on user requests or needs

4: Optional how to handle addition security validation like 2fa Email code if enabled by the user after first login

5: Support for Native App framework Capacitorjs, Ionic, Cordova, Nativescript & More

Default Provider: Apple Facebook Google Github Instagram Twitter Bitbucket Linkedin Vkontakte Live Oauth1, Oauth2 & Twitch

NOTE: PR of New Provider can be submitted default location src/providers/index.ts(https://github.com/diadal/universal-social-auth/src/providers/index.ts)

Supported OAuth providers and configurations

Installation

First install Socialite

NOTE: Make sure you config your Socialite configuration data in services.php & .env for more details check https://socialiteproviders.netlify.com/

composer require laravel/socialite
or
composer require socialiteproviders/github

Next install universal-social-auth

npm install universal-social-auth

Vue3 Example (https://github.com/diadal/universal-social-auth-test-vue3)

Vue2 Example (https://github.com/diadal/universal-social-auth-test-vu2)

Usage

for Native App Frameworks config your APp to support Deep Link I used capacitorjs as an example and use Emitter to pass data to page component

// Optional for Native App main.[js|ts] or app.[js|ts]
 App.addListener('appUrlOpen', function (data) {
    const slug = data.url.split('.com').pop()
    if (slug) {
     /// redirectUri: 'https://myapp.com/auth/github/callback'
       const callback ='/callback' //string from reirectUri make this unique
       const code = slug.split('code=').pop()
       const checker = slug?.toString().includes(callback) && code
       if (checker) {
         emitter.emit('OauthCall', code)
       }
       else{
            router.push({
            path: slug
          })
       }

    }
  })


import axios, { AxiosInstance } from 'axios'
import UniversalSocialauth from 'universal-social-auth'
or import {UniversalSocialauth} from 'universal-social-auth'


`Vue3` declare module '@vue/runtime-core' {
            interface ComponentCustomProperties {
                $axios: AxiosInstance;
                $Oauth: UniversalSocialauth;

            }
            }
const options = {
  providers: {
    apple: {
      nonce: '**************',
      state: '**************',
      clientId: '**************',
      responseMode: 'form_post',
      response_type: 'code  id_token',
      optionalUrlParams: ['response_mode', 'use_popup', 'state'],
      usePopup: true,
      redirectUri: 'https://myapp.com/auth/github/callback'
    },
    github: {
      clientId: '**************',
      redirectUri: 'https://myapp.com/auth/github/callback'
    },
    google: {
      clientId: '***************',
      redirectUri: 'https://myapp.com/auth/google/callback'
    },
    facebook: {
      clientId: '************',
      redirectUri: 'https://myapp.com/auth/facebook/callback'
    },
    twitter: {
      url: 'https://myapp.com/auth/twitter',
      clientId: '********',
      redirectUri: 'https://myapp.com/auth/twitter/callback'
    }
  }
}

const Oauth:UniversalSocialauth = new UniversalSocialauth(axios, options)


`Vue2` Vue.prototype.$axios = axios
`Vue2` Vue.prototype.$Oauth = Oauth

`Vue3` app.config.globalProperties.$Oauth = Oauth
`Vue3` app.config.globalProperties.$axios = axios

`Other Framework` based on your global declaration

Button Method 1 note the null value this equal null if you import all provider

<button @click="useAuthProvider('github', null)">auth Github</button>
<button @click="useAuthProvider('facebook', null)">auth Facebook</button>
<button @click="useAuthProvider('google', null)">auth Google</button>
<button @click="useAuthProvider('twitter', null)">auth Twitter</button>

Button Method 2 note the note provider name eg: Github each provider must the imported individually & custom provider can be added eg: <button @click="useAuthProvider('mycustom', Mycustom)">auth Mycustom</button> this give ability to add none existing Provider

<button @click="useAuthProvider('github', Github)">auth Github</button>
<button @click="useAuthProvider('facebook', Facebook)">auth Facebook</button>
<button @click="useAuthProvider('google', Google)">auth Google</button>
<button @click="useAuthProvider('twitter', Twitter)">auth Twitter</button>
<button @click="useAuthProvider('mycustom', Mycustom)">auth Mycustom</button>

View Component

<script lang="ts">

import {
  getCurrentInstance,
  ComponentCustomProperties,
  ComponentInternalInstance,
} from 'vue';
import { ProderT } from 'universal-social-auth/dist/providers'

// Button Method 1
import { Providers} from 'universal-social-auth'

// Button Method 2
import { Github, Facebook, Google , Twitter} from 'universal-social-auth'
const MycustomProvider = {

    // Mycustom provider datas
}

const globalProperties = <ComponentInternalInstance>getCurrentInstance();
const box: ComponentCustomProperties = <ComponentCustomProperties>(
  globalProperties.appContext.config.globalProperties
);

// Below are the functions to use inside you export default be `Vue3 Setup()` or `Vue2 data()` or other `Framework`

 function useAuthProvider (provider:string, proData:Record<string, unknown>| null) {
      const pro = <ProderT>proData

      const ProData = pro || <ProderT>Providers[provider]
      box.$Oauth.authenticate(provider, ProData).then((response) => {
        const rsp:{code:string} = <{code:string}>response
        if (rsp.code) {
          responseData.value.code = rsp.code
          responseData.value.provider = provider
          useSocialLogin()
        }
      }).catch((err:unknown) => {
        console.log(err)
      })
    }


async function useLoginFirst (e: User) {
    // this sample of how to pust user data to my store
      const firstlogin: boolean = await box.$auth.firstlogin(e)
      if (firstlogin) {
        
       const msg = `Welcome To My App`
        alert(msg)
        await box.$router.push({ name: 'dashboard' })
        return
      }
    }

  function useSocialLogin () {
      // otp from input Otp form
      // hash user data in your backend with Cache or save to database
      const pdata = { code: responseData.value.code, otp: data.value.tok, hash: hash.value }
      box.$axios.post('/social-login/' + responseData.value.provider, pdata).then(async (response) => {
          // `response` data base on your backend config
        if (response.data.status === 444) {
          hash.value = response.data.hash
          fauth.value = true // Option show Otp form incase you using 2fa or any addition security apply to your app you can handle all that from here

        }else if (response.data.status === 445) {
          //do something Optional

        }else {

          await useLoginFirst(response.data.u)
        }
      }).catch((err:unknown) => {

        console.log(err)
      })
    }
// Optional for Native App listen to the event `OauthCall` from your page component main.[js|ts] or app.[js|ts]

    emitter.on('OauthCall',  (e) => {
      if(e){

        responseData.value.code = e
        useSocialLogin()

      }


    })


</script>

NOTE: Dont forget to off emitter beforeDestroy or onBeforeUnmount if you are building for Native App also must redirect back to the same domain

Vue Router

        {
          path: '/auth/:provider/callback',
          component: {
            template: '<div class="auth-component"></div>'
          }
        },

Vue is Done let move to backend config Laravel with Socialite

Laravel Router

Route::post('sociallogin/{provider}', 'Auth\AuthController@SocialSignup');

// you need the post method to work with apple
Route::post('auth/{provider}/callback', 'Auth\AuthAppController@AppleCode');

Route::get('auth/{provider}/callback', 'OutController@index')->where('provider', '.*');

OutController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class OutController extends Controller
{


    public function __construct()
    {

    }


    public function index()
    {

      return view('welcome');

    }
}

Auth\AuthController

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

use Socialite;

class AuthController extends Controller
{


    public function __construct()
    {

    }

  // note if you are using sign by apple you need this
    public function AppleCode(Request $r, $provider)
    {
        //You can get apple code or finish all the process once
        return redirect($r->url() . '?code=' . $r->code);
    }

    public function SocialSignup(Request $r, $provider)
    {
        $validator = Validator::make($r->all(), [
            'code' => 'nullable|string',
            'hash' => 'nullable|string',
            'otp' => 'nullable|numeric',
            'token' => 'nullable|string',
            'secret' => 'nullable|string',

        ]);
        if ($validator->fails()) {
            return [
                'message' => 'Incorrect Data Posted',
                'status' => 445,
            ];
        }

        $hash = $r->hash ?? null;
        $hashuser = Cache::get($hash);
        if ($hashuser) {
            return $this->SocialSignupNext($r, $hashuser);
        }
        try {
            // Socialite will pick response data automatic
            $user = Socialite::driver($provider)->stateless()->user();
           $token = $user->token ?? null;
            $refreshToken = $user->refreshToken ?? null;
            $expiresIn = $user->expiresIn ?? null;
            $tokenSecret = $user->tokenSecret ?? null;
            $id = $user->id ?? $user->getId();
            $nickname = $user->nickname ?? $user->getNickname();
            $firstName = $user->name->firstName ?? null;
            $lastName = $user->name->lastName ?? null;
            $name = $user->name ?? $firstName . ' ' . $lastName ?? null;
            $email = $user->getEmail();
            $profileImage = $user->getAvatar();

             $data =  [
                'name' => $name,
                'nickname' => $nickname,
                'profileImage' => $profileImage,
                'username' => '',
                'email' => $email,
                'provider' => $provider,
                'provider_id' => $id,
                'token' => $token,
                'tokenSecret' => $tokenSecret,
                'refreshToken' => $refreshToken,
                'expiresIn' => $expiresIn,

            ];
        // this is optional can be skip you can return your user data from here
        if($email){

        return $this->SocialSignupNext($r, $data);

        }

        } catch (\Throwable $th) {
            logger($th);
        }

        return [
                'message' => 'Unknow Error',
                'status' => 445,
            ];
    }


    public function SocialSignupNext($request, $userdata)
    {
        $email = $userdata['email'];
        $provider = $userdata['provider'];
        $provider_id = $userdata['provider_id'];
        $name = $userdata['name'];
        $usr = User::where('email', $email)->get();

        $user =  $usr->where('provider', $provider)
            ->where('provider_id', $provider_id)
            ->first();

        if ($user) {
            return $this->SocialLogin($request, $user);
        }
        $user = $usr->first();
        if ($user) {
            $user->update([

                'provider' => $provider,
                'provider_id' => $provider_id,

            ]);
            return $this->SocialLogin($request, $user);
        }
        $u =  User::create([
            'name' => $name,
            'email' => $email,
            'provider' => $provider,
            'provider_id' => $provider_id,

        ]);
        // this is optional can be skip you can return your user data from here
        return $this->SocialLogin($request, $u);
    }



    public function SocialLogin($r, $user)
    {

        $hashid =  Str::random(12);

        // to verify additional security
        if ($user->google2fa_secret && !$this->mlean($r->otp)) {
            Cache::put($hashid, $user, now()->addMinutes(15));
            return [
                'message' => 'Unauthorized',
                'status' => 444,
                'hash' => $hashid
            ];
        }
        // check 2fa
        if ($this->mlean($r->otp)) {
            $g = \Google2FA::verifyKeyNewer(
                $user->google2fa_secret,
                ($this->mlean($r->otp)),
                $user->google2fa_ts
            );
            if (!$g) {
                return [
                    'message' => '2FA Expired Or Incorrect Code',
                    'status' => 445
                ];
            } else {
                $user->update([

                    'google2fa_ts' => $g

                ]);
                // optional incase you are using passport oAuth
                $tokenResult = $user->createToken('Personal Access Token');
                $token = $tokenResult->token;
                $token->save();
                return [
                    'u' => [
                        'data' => $tokenResult->accessToken,
                        'user' => $user
                    ]
                ];
            }
        }

        $tokenResult = $user->createToken('Personal Access Token');
        $token = $tokenResult->token;
        $token->save();

        return [
            'u' => [
                        'data' => $tokenResult->accessToken,
                        'user' => $user
                    ]
        ];
    }


}

EventServiceProvider.php for Apple

<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        \SocialiteProviders\Manager\SocialiteWasCalled::class => [
            // ... other providers
            'SocialiteProviders\\Twitch\\TwitchExtendSocialite@handle',
            'SocialiteProviders\\Apple\\AppleExtendSocialite@handle',

        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        //
    }
}

services.php

<?php

return [

   // .....

    'twitter' => [
        'client_id'     => env('TWITTER_ID'),
        'client_secret' => env('TWITTER_SECRET'),
        'redirect'      => env('TWITTER_URL'),
    ],

    'facebook' => [
        'client_id'     => env('FACEBOOK_ID'),
        'client_secret' => env('FACEBOOK_SECRET'),
        'redirect'      => env('FACEBOOK_URL'),
    ],

    'github' => [
        'client_id'     => env('GITHUB_ID'),
        'client_secret' => env('GITHUB_SECRET'),
        'redirect'      => env('GITHUB_URL'),
    ],

    'google' => [
        'client_id'     => env('GOOGLE_ID'),
        'client_secret' => env('GOOGLE_SECRET'),
        'redirect'      => env('GOOGLE_URL'),
    ],

    'vkontakte' => [
        'client_id'     => env('VKONTAKTE_KEY'),
        'client_secret' => env('VKONTAKTE_SECRET'),
        'redirect'      => env('VKONTAKTE_REDIRECT_URI'),
    ],
];

.env


TWITTER_ID=Your ID
TWITTER_SECRET=Your Secret
TWITTER_URL=https://example.com/auth/twitter/callback

FACEBOOK_ID=Your ID
FACEBOOK_SECRET=Your Secret
FACEBOOK_URL=https://example.com/auth/facebook/callback

GITHUB_ID=Your ID
GITHUB_SECRET=Your Secret
GITHUB_URL=https://example.com/auth/github/callback

GOOGLE_ID=Your ID
GOOGLE_SECRET=Your Secret
GOOGLE_URL=https://example.com/auth/google/callback

VKONTAKTE_KEY=Your ID
VKONTAKTE_SECRET=Your Secret
VKONTAKTE_REDIRECT_URI=https://example.com/auth/vkontakte/callback

VerifyCsrfToken Middleware

you may need to disable Csrf for the route if you receive Error: Request failed with status code 419

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [

        '/sociallogin/*'
    ];
}

if any issue check

if you wish you can buy me a coffee @ Patreon

License

The MIT License (MIT)

Copyright (c) 2018 Diadal Nig LTD

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

universal-social-auth's People

Contributors

diadal avatar dependabot[bot] avatar snyk-bot avatar kerrialn avatar

Stargazers

Savaş Türkoğlu avatar Pratik Rane avatar silvesterwali avatar KrenZfer avatar Diego  avatar Ziggy Moens avatar  avatar Tao Zuhong avatar Alex avatar  avatar  avatar  avatar Gonçalves Hugo avatar Tristan WAGNER avatar  avatar  avatar pdina avatar German Bosio avatar Odiachi Daniel Tobechukwu avatar Eric Li avatar  avatar krismawan avatar  avatar Collin Pedersen avatar Priit avatar Zouheir Layine avatar g-30 avatar Aleksandr G avatar  avatar Alex Stewart avatar Ruslan Voroshchuk avatar Calvin Job Puram avatar Alexey Lobyak avatar Eslam Nemr avatar  avatar Olivier avatar Vadim Neznaikin avatar Alex  Marquezini avatar Steven Hardy avatar  avatar

Watchers

Evangelos Dimitriadis avatar James Cloos avatar Steven Hardy avatar  avatar  avatar

universal-social-auth's Issues

Twitter does not seem to be working

Describe the bug
I am unable to log in using Twitter, but the same configuration allows me to log in with Facebook, Google, Discord, Twitch and others.

I get the error message "There is no request token for this page."

ss

config

const options = {
    providers: {
      twitter: {
        url: process.env.TWITTER_URL,
        clientId: process.env.TWITTER_ID,
        redirectUri: process.env.TWITTER_REDIRECT_URI,
      },
    },
  }
  const Oauth = new UniversalSocialauth(axios, options)
  app.config.globalProperties.$Oauth = Oauth
  app.provide('$Oauth', Oauth)

component

import { inject } from 'vue'
import { useStore } from 'vuex'
import { Github, Facebook, Google, Twitter } from 'universal-social-auth'

const Oauth = inject('$Oauth')
const store = useStore()

function useAuthProvider(provider, proData) {
  const pro = proData
  const ProData = pro || Providers[provider]
  Oauth.authenticate(provider, ProData).then((response) => {
    if (response.code) {
      responseData.value.code = response.code
      responseData.value.provider = provider
      store.dispatch('auth/socialLogin', { provider, response })
    }
  }).catch((err) => {})
}

call

@click="useAuthProvider('twitter', Twitter)"

self is not defined in Nuxt 3 project

I'm tryng to integrate the package in a Nuxt 3 app via plugins. Unfortunately I get this error:

[nuxt] [request error] [unhandled] [500] self is not defined     

Can you do an example of Nuxt 3 implementation? Thanks you.

Add full Vue3 example and demo

Is your feature request related to a problem? Please describe.
the Vue3 example is not clear, follow the guide can't done the thing.

Describe the solution you'd like
Add a full Vue3 example and live demo to show the final effec, thanks.

Describe alternatives you've considered
Make the different framework's example clear.

Additional context
Thank for your great work.

Thank you very much & Setup

Hello @diadal

I just want to say thank you very much for getting this new version of vue-social-auth live.

Unfortunately being a Typescript novice, I am completely unsure how to set this up now within my Vue 2 SPA. Your README.md is good, but unfortunately, I am unable to follow it to be able to use this package within my application.

Do you have any guides or example projects that I can take a look at please?

all examples links are missing in README.md

Is your feature request related to a problem? Please describe.
the examples are not present. all links are gone.

Describe the solution you'd like
i think example should added in the repository itself. at least to the readme file itself. like in vue-social-auth

Describe alternatives you've considered
same as above

Additional context
Add any other context or screenshots about the feature request here.

Does this work for Twitter?

It doesn't look like you are defining a POST route handler for auth/twitter anywhere? This is required, no?

Prompt mobile app instead of browser

Firstly, thank you for this great package.

I have one question: When trying to use the social login on mobile (for example Facebook) it will prompt the Facebook login screen in the browser instead of the Facebook app. Because I am not logged in to Facebook in the browser, but only in the actual mobile app, I still need to fill in my username and password. Is there a way to prompt the Facebook app instead of the browser login screen so it will automatically sign me in using the app?

Plugin fails in NustJs

Describe the bug
I get this error self is not defined when I import the the provider in a component

Kindly review why or give a guide on how to use in NuxtJs

Last two digits of clientId are being replaced with 00

Describe the bug
When creating a Discord social login, universal-social-auth will cut off the last two digital of my Discord clientId which ends with 77 but it replaces them with zeros. If I replace the last two zeros in the URI with 77, everything works. Is there a setting for clientId length that I am missing?

const Discord = {
  name: 'discord',
  url: '/auth/discord',
  authorizationEndpoint: 'https://discord.com/api/oauth2/authorize',
  tokenURL: 'https://discord.com/api/oauth2/token',
  redirectUri: window.location.origin,
  scopeDelimiter: '%20',
  sessionKey: 'oauth:discord',
  oauthType: '2.0',
  requiredUrlParams: ['scope'],
  scope: ['identify', 'email'],
  popupOptions: { width: 495, height: 645 },
}

export { Discord }

https://discord.com/oauth2/authorize?response_type=code&client_id=*****************00&redirect_uri=https://my.site:8080/auth/discord/callback&scope=identify%20email

I did a little bit of investigation and it looks like this issue has to do with Discord using a completely numeric clientId.

This piece of code seems to be the issue.

export function joinUrl (baseUrl: string, url: string): string {
if (/^(?:[a-z]+:)?\/\//i.test(url)) {
return url
}
const joined = [baseUrl, url].join('/')
const normalize = function (str: string) {
return str
.replace(/[/]+/g, '/')
.replace(/\/\?/g, '?')
.replace(/\/#/g, '#')
.replace(/:\//g, '://')
}
return normalize(joined)
}

I made this codepen that demonstrates the issue.

https://codepen.io/whoacowboy/pen/VwVrBpr

I think it has to do with the the number being larger than JavaScipt can handle as an integer.

https://stackoverflow.com/questions/4557509/javascript-summing-large-integers

I tried wrapping the clientId in quotes and that did not fix the issue.

Response code never returned

Bug
When I try to use the social auth with google, it opens a popup but it redirects to the redirect URI in the popup but never closes the popup or returns the response from where I'm trying to log in

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.