fix(docs): overhaul Ionic Vue tutorial auth flow and example app (#47142)

## I have read the CONTRIBUTING.md file.
YES

## What kind of change does this PR introduce?
Bug fix, docs update

Closes DOCS-353.

## What is the current behavior?
- Linear item: Overhaul Ionic Vue tutorial
- Debug email shown on login page after submit
- Login redirect / route guard issues
- Email missing on account page
- HTML5 `type="url"` blocks profile update for `example.com`
- Tutorial `.env` used `VUE_APP_SUPABASE_KEY` but app expects
`VUE_APP_SUPABASE_PUBLISHABLE_KEY`

## What is the new behavior?
- Removed debug `<p>{{ email }}</p>` from Login.vue
- Route guards in `router/index.ts`; simplified App.vue user sync
- Account email from `getClaims()`; website `type="text"`;
`useIonRouter` for logout
- Store typed as `User | null`
- Tutorial MDX env var fixed
- Audit skills moved to
[supabase/docs-agent-skills](https://github.com/supabase/docs-agent-skills):
[`audit-ionic-vue-tutorial`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-ionic-vue-tutorial/SKILL.md),
[`audit-docs-tutorials`](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-docs-tutorials/SKILL.md)
— both require **live Supabase platform** E2E (not just build/lint)

**Tutorial preview:** [Build a User Management App with Ionic
Vue](https://supabase.com/docs/guides/getting-started/tutorials/with-ionic-vue)


## Additional context
- Verification (author): `npm install && npm run build` — **pass**
(warnings only)
- E2E: profiles migration applied to project `moijyfpvgnmgoxvwcikq`; add
`.env` from `.env.example` with publishable key for full auth flow test

### Test plan
- [ ] `cd examples/user-management/ionic-vue-user-management && npm
install && npm run build`
- [ ] Confirm tutorial MDX `.env` uses
`VUE_APP_SUPABASE_PUBLISHABLE_KEY`
- [ ] Copy Project URL + publishable key into `.env`
- [ ] `npm run dev` — login page shows no email after magic-link submit
- [ ] Magic link redirects to `/account`
- [ ] Email displays on account page
- [ ] Update profile with `example.com` and `https://example.com` — both
save
- [ ] Logout returns to `/`; `/account` blocked without auth
- [ ] Install audit skills from
[docs-agent-skills](https://github.com/supabase/docs-agent-skills) (`ln
-sf
~/GitHub/supabase/docs-agent-skills/.claude/skills/audit-ionic-vue-tutorial
~/.claude/skills/audit-ionic-vue-tutorial` and same for
`audit-docs-tutorials`), then run
[audit-ionic-vue-tutorial](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-ionic-vue-tutorial/SKILL.md)
platform E2E (magic link, profile update verified via SQL)
- [ ] Run
[audit-docs-tutorials](https://github.com/supabase/docs-agent-skills/blob/main/.claude/skills/audit-docs-tutorials/SKILL.md)
— confirm platform E2E column is filled for with-ionic-vue


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Improved authentication handling in the Ionic Vue user-management
example, with smoother sign-in/sign-out redirects and account access
checks.
  * Updated account details to display the user’s email more reliably.

* **Bug Fixes**
* Adjusted navigation so authenticated users are routed to the account
page and unauthenticated users are sent back to the home page.

* **Documentation**
* Updated setup instructions and example environment variables for the
Supabase integration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Nik Richers <nik@validmind.ai>
This commit is contained in:
Nik RichersandNik Richers authored and GitHub committed 2026-06-26 09:17:33 -07:00
1 parent a9565c1645
commit 032a368221
9 files changed
+51 -32

No files matched your search

@@ -0,0 +1,2 @@
VUE_APP_SUPABASE_KEY=YOUR_SUPABASE_KEY
VUE_APP_SUPABASE_URL=YOUR_SUPABASE_URL
@@ -25,7 +25,7 @@ This demonstrates how to use:
The Vercel deployment will guide you through creating a Supabase account and project. After installation of the Supabase integration, all relevant environment variables will be set up so that the project is usable immediately after deployment 🚀.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?project-name=supabase-ionic-vue&repo-name=supabase-ionic-vue&envDescription=Find%20the%20Supabase%20URL%20and%20key%20in%20the%20your%20auto-generated%20docs%20at%20app.supabase.io&repository-url=https%3A%2F%2Fgithub.com%2Fmhartington%2Fsupabase-ionic-vue%2Ftree%2Fmain&env=VUE_APP_SUPABASE_URL%2CVUE_APP_SUPABASE_PUBLISHABLE_KEY)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?project-name=supabase-ionic-vue&repo-name=supabase-ionic-vue&envDescription=Find%20the%20Supabase%20URL%20and%20key%20in%20the%20your%20auto-generated%20docs%20at%20app.supabase.io&repository-url=https%3A%2F%2Fgithub.com%2Fmhartington%2Fsupabase-ionic-vue%2Ftree%2Fmain&env=VUE_APP_SUPABASE_URL%2CVUE_APP_SUPABASE_KEY)
## Build from scratch
@@ -55,7 +55,7 @@ Update your environment file `environment.ts`
export const environment = {
// ...
supabaseUrl: "YOUR_SUPABASE_URL",
supabasePublishableKey: "YOUR_SUPABASE_PUBLISHABLE_KEY"
supabaseKey: "YOUR_SUPABASE_KEY"
};
```
@@ -5,26 +5,27 @@
</template>
<script setup lang="ts">
import { IonApp, IonRouterOutlet, useIonRouter } from '@ionic/vue';
import { IonApp, IonRouterOutlet } from '@ionic/vue';
import { onUnmounted } from 'vue';
import { store } from './store';
import router from './router';
import { supabase } from './supabase';
const router = useIonRouter();
async function syncAuthRedirect() {
const { data } = await supabase.auth.getClaims();
const claims = data?.claims;
const path = router.currentRoute.value.path;
supabase.auth.getClaims().then(({ data: { claims } }) => {
store.user = claims;
});
if (claims && path === '/') {
router.replace('/account');
} else if (!claims && path === '/account') {
router.replace('/');
}
}
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, session) => {
store.user = session?.user ?? null;
if (session?.user) {
router.replace('/account');
} else {
router.replace('/');
}
} = supabase.auth.onAuthStateChange(() => {
syncAuthRedirect();
});
onUnmounted(() => {
@@ -2,6 +2,8 @@ import { createRouter, createWebHistory } from '@ionic/vue-router'
import { RouteRecordRaw } from 'vue-router'
import LoginPage from '../views/Login.vue'
import AccountPage from '../views/Account.vue'
import { supabase } from '../supabase'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
@@ -20,4 +22,21 @@ const router = createRouter({
routes,
})
router.beforeEach(async (to, _from, next) => {
const { data } = await supabase.auth.getClaims()
const claims = data?.claims
if (to.path === '/account' && !claims) {
next('/')
return
}
if (to.path === '/' && claims) {
next('/account')
return
}
next()
})
export default router
@@ -1,6 +0,0 @@
import type { Claims } from '@supabase/supabase-js'
import { reactive } from 'vue'
export const store = reactive<{ user: Claims | null }>({
user: null,
})
@@ -1,7 +1,7 @@
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = process.env.VUE_APP_SUPABASE_URL
const supabasePublishableKey = process.env.VUE_APP_SUPABASE_PUBLISHABLE_KEY
const supabaseKey = process.env.VUE_APP_SUPABASE_KEY
if (!supabaseUrl) {
throw new Error(
@@ -9,10 +9,10 @@ if (!supabaseUrl) {
)
}
if (!supabasePublishableKey) {
if (!supabaseKey) {
throw new Error(
'Environment variable VUE_APP_SUPABASE_PUBLISHABLE_KEY is not set. Please define it before starting the application.'
'Environment variable VUE_APP_SUPABASE_KEY is not set. Please define it before starting the application.'
)
}
export const supabase = createClient(supabaseUrl, supabasePublishableKey)
export const supabase = createClient(supabaseUrl, supabaseKey)
@@ -12,7 +12,7 @@
<ion-item>
<ion-label>
<p>Email</p>
<p>{{ store.user?.email }}</p>
<p>{{ email }}</p>
</ion-label>
</ion-item>
@@ -28,7 +28,7 @@
<ion-item>
<ion-input
type="url"
type="text"
name="website"
label="Website"
label-placement="stacked"
@@ -48,7 +48,6 @@
</template>
<script setup lang="ts">
import { store } from '@/store';
import { supabase } from '@/supabase';
import {
IonContent,
@@ -62,12 +61,13 @@ import {
IonItem,
IonButton,
IonLabel,
useIonRouter,
} from '@ionic/vue';
import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import Avatar from '../components/Avatar.vue';
const router = useRouter();
const router = useIonRouter();
const email = ref('');
const profile = ref({
username: '',
@@ -83,6 +83,8 @@ async function getProfile() {
const { data: { claims } } = await supabase.auth.getClaims();
if (!claims) throw new Error('No user logged in');
email.value = (claims.email as string) ?? '';
const { data, error, status } = await supabase
.from('profiles')
.select(`username, website, avatar_url`)
@@ -138,7 +140,7 @@ async function signOut() {
try {
const { error } = await supabase.auth.signOut();
if (error) throw error;
await router.push('/');
router.push('/', 'forward', 'replace');
} catch (error: any) {
toast.message = error.message;
await toast.present();
@@ -28,7 +28,6 @@
</div>
</form>
</ion-list>
<p>{{ email }}</p>
</ion-content>
</ion-page>
</template>