Integrate authentication into Vue.js + API
This guide shows how to create a simple Vue.js application and secure it with authentication powered by Ory. You can use this guide with both The Ory Network and self-hosted Ory software.
This guide is perfect for you if:
- You have Vue.js installed.
- You want to build an app using Vue.js.
- You want to give access to your application to signed-in users only.
Before you start, watch this video to see the user flow you're going to implement:
You can find the code of the sample application here.
Create Vue.js app
First, create a new Vue.js project. Run:
npx @vue/cli create ory-spa --preset "Default (Vue 3)"
cd ory-spa
Install Ory SDK
Use npm
to install the Ory SDK:
npm i --save @ory/client
Check for session in component
With a Vue.js project created, add the Ory logic to the application. The application should recognize whether the user is signed in and if they have a valid Session.
Depending on whether the user has a valid Session, the application should show registration and login links or account settings and logout links.
To implement that behavior, modify the existing HelloWorld.vue
component to include the appropriate logic:
<template>
<div class="main">
<h1>{{ msg }}</h1>
<div v-if="!session">
<p>Click on "login" or "Sign Up" below to sign in.</p>
<li><a :href="basePath + '/ui/login'" data-testid="sign-in">Login</a></li>
<li>
<a :href="basePath + '/ui/registration'" data-testid="sign-up"
>Sign Up</a
>
</li>
</div>
<h3 v-if="session">Calling <code>toSession()</code></h3>
<div v-if="session" class="long">
<p>
Use the SDK's <code>toSession()</code> call to receive the session
information, for example the authentication methods used:
</p>
<pre><code data-testid='ory-response'>{{ session.authentication_methods }}</code></pre>
</div>
<h3 v-if="apiResponse">API Response</h3>
<div v-if="apiResponse" class="long">
<p>
Or make authenticated AJAX calls to your API using <code>fetch()</code>:
</p>
<pre><code data-testid='api-response'>{{ apiResponse }}</code></pre>
</div>
<h3 v-if="session">Common Actions</h3>
<ul v-if="session">
<li><a :href="logoutUrl" data-testid="logout">Logout</a></li>
<li>
<a :href="basePath + '/ui/settings'" data-testid="settings">Settings</a>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://www.ory.sh">Ory Website</a></li>
<li><a href="https://github.com/ory">Ory GitHub</a></li>
<li><a href="https://www.ory.sh/docs">Documentation</a></li>
</ul>
</div>
</template>
<script>
import { FrontendApi, Configuration } from "@ory/client"
// The basePath points to the location of Ory's APIs.
// You can use https://<slug>.projects.oryapis.com/ here because cookies can not
// easily be shared across different domains.
//
// In the next step, we will run a process to mirror Ory's APIs
// on your local machine using the Ory Tunnel at http://localhost:4000
const basePath = process.env.VUE_APP_ORY_URL || "http://localhost:4000"
const ory = new FrontendApi(
new Configuration({
basePath,
baseOptions: {
// Ensures we send cookies in the CORS requests.
withCredentials: true,
},
}),
)
const apiUrl = process.env.VUE_APP_API_URL || "http://localhost:8081"
export default {
name: "HelloWorld",
props: {
msg: String,
},
data() {
return {
session: null,
logoutUrl: null,
apiResponse: null,
basePath,
}
},
mounted() {
// Fetch the session directly from Ory
ory.toSession().then(({ data }) => {
this.session = data
// If the user is logged in, we want to show a logout link!
ory.createBrowserLogoutFlow().then(({ data }) => {
this.logoutUrl = data.logout_url
})
})
// Or make an authenticated request to your API
fetch(apiUrl + "/api/hello", {
// Do not forget to set this - it is required to send the session cookie!
credentials: "include",
})
.then(
(res) =>
res.ok &&
res.json().then((res) => {
this.apiResponse = res
}),
)
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.main {
max-width: 400px;
margin: 0 auto;
}
</style>
Run Ory APIs on localhost
The Ory security model uses HTTP cookies to manage sessions, tokens, and cookies. To do that, Ory APIs must be exposed on the same domain as your application.
In the case of this example, the domain of your application, which runs on your local machine, is localhost
.
When developing locally, use either localhost
or 127.0.0.1
, but not both. Although technically these mean the same thing,
they're different hostnames.
Using both interchangeably in your code can cause problems with the origin of HTTP cookies sent by the application. When that happens Ory APIs might not be able to read the cookies sent by your app.
To get your application that runs locally and Ory APIs on the same domain, use Ory Tunnel - a development tool bundled with Ory CLI. It's like a microservice - an authentication API server on your domain!
npx @ory/cli tunnel --dev http://localhost:8080
Ory APIs are now mirrored on http://localhost:4000
. Use that URL as the baseUrl
for the @ory/client
SDK (see
HelloWorld.vue
code above). The --dev
flag disables security checks for easier integration and should not be used when
deploying the Tunnel to a staging environment.
Run your Vue.js app
Now that your app is ready, it's time to run it!
First, set up your Ory Network SDK URL to connect with the Ory APIs in your project:
- macOS
- Linux
- Windows CMD
- Windows Powershell
- Self-Hosted Ory Kratos
# This is a public Ory Network Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up to Ory Network at
#
# https://console.ory.sh/registration
#
# and create a free Ory Network Project to see your own configuration embedded in code samples.
export ORY_SDK_URL=https://{project.slug}.projects.oryapis.com
# This is a public Ory Network Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up to Ory Network at
#
# https://console.ory.sh/registration
#
# and create a free Ory Network Project to see your own configuration embedded in code samples.
export ORY_SDK_URL=https://{project.slug}.projects.oryapis.com
# This is a public Ory Network Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up to Ory Network at
#
# https://console.ory.sh/registration
#
# and create a free Ory Network Project to see your own configuration embedded in code samples.
set ORY_SDK_URL=https://{project.slug}.projects.oryapis.com
# This is a public Ory Network Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up to Ory Network at
#
# https://console.ory.sh/registration
#
# and create a free Ory Network Project to see your own configuration embedded in code samples.
$Env:ORY_SDK_URL = "https://{project.slug}.projects.oryapis.com"
Clone and run Ory Kratos locally
git clone --depth 1 --branch master https://github.com/ory/kratos.git
cd kratos
git checkout master
git pull -ff
docker-compose -f quickstart.yml -f contrib/quickstart/kratos/cloud/quickstart.yml up --build --force-recreate -d
and set the environment variable to the exposed port:
export ORY_SDK_URL=http://localhost:4433
Next, start the Next.js development server:
npm run serve
Go to localhost:8080 to access your application.
Make authenticated calls to your API
To make authenticated requests to your API there are two main components:
When making AJAX requests you must set
{"credentials": "include"}
in the options. For thefetch
method it looks like this:fetch(url, { credentials: "include" })
Your API must have a CORS middleware with
credentials: true
andAccess-Control-Allow-Origin
of your frontend app (herehttp://localhost:8080
).
Let's put this into action. Create a simple HTTP API with express. Run:
mkdir my-api
cd my-api
npm init
npm i --save @ory/client express cors
Next, create a simple API in index.js
:
const express = require("express")
const cors = require("cors")
const { FrontendApi, Configuration } = require("@ory/client")
const app = express()
const ory = new FrontendApi(
new Configuration({
// Points to the local Ory API server (Ory TunneL).
basePath: process.env.ORY_URL || "http://localhost:4000",
baseOptions: { withCredentials: true },
}),
)
app.use(
cors({
origin: process.env.UI_URL || "http://localhost:8080",
credentials: true, // <- Required for CORS to accept cookies and tokens.
}),
)
app.use((req, res, next) => {
// A simple middleware to authenticate the request.
ory
.toSession({
// This is important - you need to forward the cookies (think of it as a token)
// to Ory:
cookie: req.headers.cookie,
})
.then(({ data }) => {
req.session = data
next()
})
.catch(() => {
res.status(401)
res.json({ error: "Unauthorized" })
})
})
app.get("/api/hello", (req, res) => {
res.json({
message: "Hello from our API!",
session_id: req.session.id,
identity_traits: req.session.identity.traits,
})
})
const port = process.env.PORT || 8081
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Finally, start the server:
node index.js
See it in action
Access your single-page app at localhost:8080, sign in, and see if you can make authenticated requests to your API!
Go to production
To promote this app to production:
- Build the VueJS app and run it somewhere (for example on Vercel or Netlify)
- Deploy the API (for example, on Heroku)
- Connect your project with a custom domain
These three components must be hosted on the same top-level domain as they were on your local machine:
Component | Production | Local |
---|---|---|
Single Page App | www.example.org | localhost:8080 |
API | api.example.org | localhost:3000 |
Ory | ory.example.org | localhost:4000 |