Auth-module: multi-tenants subdomains API

Created on 29 Apr 2021  路  10Comments  路  Source: nuxt-community/auth-module

The API I need to request is multi-tenants, with tenant subdomains: tenant1.example.com/api, tenant2.example.com/api, ...
How can I configure nuxt axios and auth endpoints for users to log to their own tenant scheme (eg. tenant1.example.com/api/login)?

Is there a way via the nuxt app subdomain (tenant1.nuxtapp.com, tenant2.nuxtapp.com, ...)?

Thank you for your help!

question

All 10 comments

Looking for solutions for the same. I have a proxy set up under nuxt.config.js but need to dynamically set the proxy target based on users input during the login form. Login form has the following fields:

  • domain
  • email
  • password

My multi-tenant application is laravel using JWT. Can't seem to figure out the correct way to set the target of the proxy during runtime. Is that even possible?

So I've gone through a whole litany of different ways to do this. The most reasonable and the closest I've come is separating the domain entry into a "setup" route for first-time config storing, and then showing the login form after the domain is stored in a database. Once the domain is stored, the login form shows. I have the following plugin registered:

export default function ({ $auth }) {
  const domain = 'some.customdomain.com' // retrieved from database
  $auth.ctx.$axios.setBaseURL(domain)
  $auth.ctx.$auth.strategies.laravelJWT.options.url = domain
}

with a config that looks like this:

  axios: {
    proxy: false // tried with both true and false
  },

  auth: {
    plugins: [
      { ssr: true, src: '@/plugins/auth-axios' } // tried with both ssr true and false
    ],
    strategies: {
      laravelJWT: {
        url: 'https://www.example.com' // expecting this to be replaced in the plugin - so just a dummy value
      }
    }
  }

This doesn't work. The request is still sent to https://www.example.com

Seriously running out of ideas at this point. I've spent about 12 hours of dev time trying to get this to work. Will likely just write my own auth to handle my needs unless someone has an idea where I'm going wrong.

Hi @mindfullsilence! The problem here is that url is used only on compile. What you need to change is endpoints.*.url.

nuxt.config.js

  auth: {
    plugins: [
      { ssr: true, src: '@/plugins/auth-axios' }
    ],
    strategies: {
      laravelJWT: {
        url: '/' // we set to /, this way we don't add prefix to endpoints url
      }
    }
  }

auth-axios.js

export default function ({ $auth, $axios }) {
  const domain = 'some.customdomain.com' // retrieved from database

  $axios.setBaseURL(domain)

  const endpoints = $auth.strategies.laravelJWT.options.endpoints;

  for (const key in endpoints) {
    endpoints[key].url = domain + endpoints[key].url;
  }
}

Let me know if it worked! :)

I believe you can use proxy if you want.

You beautiful SOB. Thank you so much! Worked like a charm.

Thank you gentlemen for your answers, but you both have been too fast for me.

Would you mind summarizing the steps I need to implement, knowing that the tenant1 users may connect to tenant1.nuxtapp.com/login (I just need to get the subdomain to root to the corresponding API: tenant1.myapi.com/api

@egerard-hesperie Here's the steps I took:

Add auth-axios.js to your plugins directory:

export default function ({ $auth, $axios }) {
  const endpoints = $auth.strategies.laravelJWT.options.endpoints
  const domain = 'subdomain.nuxtapp.com' // retrieve this from your database, the request, or some other way. 

  for (const key in endpoints) {
    endpoints[key].url = endpoints[key].url.startsWith('/')
      ? endpoints[key].url.replace(/^\/+/, '') // dealing with double slashes (subdomain.nuxtapp.com//api/auth/login)
      : endpoints[key].url

    endpoints[key].url = [domain, endpoints[key].url].join('/')
  }
}

Then in your nuxt.config.js file:

const config = {
  // ...
  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/auth-next'
  ],

  axios: {
    proxy: false
  },

  auth: {
    plugins: [
      { src: '@/plugins/auth-axios' }
    ],
    strategies: {
      laravelJWT: {
        provider: 'laravelJWT',
        url: '/',
        token: {
          property: 'token',
          type: 'Bearer'
        },
        user: {
          property: 'data',
          autoFetch: true
        }
      }
    }
  }
}

Then in your login page/component (my app is using buefy components for the fields):

<template>
  <div class="card">
      <b-field label="email">
        <b-input
          v-model="formData.email"
          type="email"
          required
        />
      </b-field>

      <b-field label="password">
        <b-input
          v-model="formData.password"
          type="password"
          password-reveal
          required
        />
      </b-field>

      <b-button
        type="is-primary"
        :disabled="isDisabled"
        @click.prevent="onSubmit"
      >
        <b-loading v-model="isLoading" :is-full-page="true" :can-cancel="false" /> Login
      </b-button>
  </div>
</template>

<script>
export default {
  data () {
    return {
      isLoading: false,
      isDisabled: false,

      formError: {
        email: '',
        password: ''
      },

      formData: {
        email: '[email protected]',
        password: 'password'
      }
    }
  },

  methods: {
    onSubmit () {
      if (this.isDisabled) return

      this.formError.email = ''
      this.formError.password = ''

      this.isLoading = true
      this.isDisabled = true

      this.loginUser()
    },
    async loginUser () {

      await this.$auth.loginWith('laravelJWT', {
        data: {
          email: this.formData.email,
          password: this.formData.password
        }
      }).catch (e) {
         this.formError.email = 'Double check your email'
         this.formError.password = 'Double check your password'
      }

      // login was a success, move on to the dashboard
      if (this.$auth.loggedIn) {
        return this.$router.push('/admin')
      }

      // login failed, remove the loader and re-enable the login button
      this.isLoading = false
      this.isDisabled = false
    }
  }
}
</script>

~Just one note here, you should use provider: 'laravelJWT', since laravel/jwt was deprecated. Sorry docs are outdated. I'll fix it soon.~

~In case you're wondering why you didn't receive any errors, it's because your strategy name is exactly laravelJWT~

EDIT: I just found out that laravel/jwt is now an alias of laravelJWT. So docs are right.

@JoaoPedroAS51 Thanks, I updated above to reflect the appropriate value.

Also a small note, there's no way I can see to set up proxy to work correctly. This isn't due to auth, but rather the inability to change proxy settings via a plugin (as far as I can tell anyway). Proxy requires a target entry in the config, and the proxies are setup on compile, so no way to effect them at runtime.

Thank you @mindfullsilence this is very helpful!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Amoki picture Amoki  路  3Comments

DougHayward picture DougHayward  路  4Comments

yuwacker picture yuwacker  路  3Comments

dasisyouyu picture dasisyouyu  路  3Comments

sebmor picture sebmor  路  3Comments