mmbrian
mmbrian

Reputation: 1662

Vue.js SPA with vuex-oidc has redirect loops on release version

I have a single page application built in Vue.js 2.5 which also has OAuth2.0 support using IdentityServer4 + vuex-oidc and runs on an nginx server. Everything with my setup works fine when running the app on webpack dev server, but the release version has a redirect loop problem which I highly suspect might be due to nginx misconfiguration.

Problem: The redirect loop behavior is always the same

  1. User requests navigation to /app
  2. oidc plugin redirects to /connect/authorize?...
  3. redirect to /app/oidc-login (authorization request's redirect uri)
  4. redirect to /app (back to step 2)

For the dev server I am using a reverse proxy configured as

location /app {
    fastcgi_buffer_size 128k;
    fastcgi_buffers 4 256k;
    fastcgi_busy_buffers_size 256k;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass https://127.0.0.1:55100;
    proxy_temp_path C:/myapp/nginxRP;
}

But since I'm using history mode in router, the release version is configured as per https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations

fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
location /app {
    try_files $uri $uri/ /index.html;
}

Where the release version of the app (index.html and static files) are located at ..\nginx\html\app

Here's my vue-router configuration

const router = new Router({
    mode: "history",
    base: "/app/",
    routes: [
        {
            path: "/oidc-login",
            name: "oidcCallback",
            component: OidcCallback,
            meta: {
                isOidcCallback: true,
                isPublic: true
            }
        },
        {
            path: "/oidc-silent-login",
            name: "oidcSilentCallback",
            component: OidcSilentCallback,
            meta: {
                isOidcCallback: false,
                isPublic: true
            }
        },
        {
            path: "/",
            name: HOME_PAGE_TITLE,
            component: Main
        },
        {
            path: "*",
            name: "Page Not Found",
            component: NotFound
        }
    ]
});

And the OidcCallback components is

<template>
  <div></div>
</template>

<script>
import { mapActions } from "vuex";
import { OIDC_MODULE_NAMESPACE } from "../../store/store";

export default {
    name: "OidcCallback",
    methods: {
        ...mapActions(OIDC_MODULE_NAMESPACE, [
            "oidcSignInCallback"
        ])
    },
    mounted () {
        this.oidcSignInCallback()
            .then((redirectPath) => {
                this.$router.push(redirectPath);
            })
            .catch((err) => {
                console.error(err);
                this.$router.push("/signin-oidc-error"); // TODO
            });
    }
};
</script>

I've configured vuex-oidc pretty much exactly as instructed in https://github.com/perarnborg/vuex-oidc/wiki#how-to-implement-vuex-oidc except that I dynamically add the oidcStore module to vuex.

Since everything just works in the dev server and I already think this is an nginx issue, I'm not sure providing what other parts of my code/setup would be helpful but please let me know in case I'm missing something and I'll share more.

Thanks

Upvotes: 2

Views: 1953

Answers (1)

mmbrian
mmbrian

Reputation: 1662

As I suspected earlier, the problem was in fact a misconfiguration on the nginx part. basically the webserver was stripping the id_token parameter returned from IdentityServer (and any other query parameters) which resulted in the redirect loop. There are two solution to this. the easy solution is to add a rewrite rule to nginx configuration and basically replace it with something like

location ^~ /app/ {
    if (!-f $request_filename) {
        rewrite ^/app/(.*)$ /app/index.html;
    }
}

So that it correctly passes every url query to the Vue app which is then handled by Vue-Router and the vuex-oidc middleware (no changes required in setting up vue-router, only the above nginx configuration is enough). Another solution is to use a "Front Controller Pattern" design and pass the complete uri with its arguments to the SPA. This still requires configuring nginx a bit differently like

location /app {
    try_files $uri $uri/ /index.html?route=$uri&$args;
}

No more rewrites, but additionally imposes special treatment in Vue-Router to perform navigation from there using a navigation guard and this route query similar to what is proposed here.

Upvotes: 1

Related Questions