Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/common/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { connectScryptedClient, getCurrentBaseUrl, logoutScryptedClient, redirectScryptedLogin, ScryptedClientLoginError, ScryptedClientOptions, ScryptedClientStatic } from '@scrypted/client/src/index';
import { checkScryptedClientLogin, connectScryptedClient, getCurrentBaseUrl, logoutScryptedClient, redirectScryptedLogin, ScryptedClientLoginError, ScryptedClientOptions, ScryptedClientStatic } from '@scrypted/client/src/index';
import { timeoutFunction, timeoutPromise } from '@scrypted/common/src/promise-utils';
import { sleep } from "@scrypted/common/src/sleep";
import { computed, reactive, ref, shallowRef } from 'vue';
Expand All @@ -14,6 +14,9 @@ export let clientPromise: Promise<ScryptedClientStatic> | undefined;
export const isLoggedIn = ref(true);
export const hasLogin = ref<boolean | undefined>();
export const cloudLoginRedirect = ref<string>();
export const authType = ref<string | undefined>(undefined);
export const oidcLinked = ref(false);
export const oidcEnabled = computed(() => !!(authType.value?.includes('oidc')));

// export const SCRYPTED_SERVER = window.location.hostname === 'beta.scrypted.app' ? 'home-dev.scrypted.app' : 'home.scrypted.app';
export const SCRYPTED_SERVER = 'home.scrypted.app';
Expand Down Expand Up @@ -238,6 +241,10 @@ export async function connectClient(options: ScryptedClientOptions): Promise<Scr
const self = connectedClient.value = await clientPromise;
console.log(connectedClient.value);
isLoggedIn.value = true;
checkScryptedClientLogin({ baseUrl: getBaseUrl() }).then(r => {
if (r.authType) authType.value = r.authType;
if (r.oidcLinked !== undefined) oidcLinked.value = r.oidcLinked;
}).catch(() => { });

self.onClose = () => {
console.error('Scrypted client closed');
Expand Down
72 changes: 48 additions & 24 deletions src/common/components/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,59 @@
<v-card-subtitle v-if="loginHostname" style="text-align: center;" class="scrypted-subtitle2">Log into: {{
loginHostname }}</v-card-subtitle>

<v-text-field class="ml-8 mr-8 mt-8 mb-8" density="compact" variant="outlined" v-model="username"
autocorrect="off" autocapitalize="off" spellcheck="false" label="User Name" persistent-placeholder
hide-details></v-text-field>
<v-text-field class="ml-8 mr-8 mb-8" density="compact" variant="outlined" v-model="password" type="password"
:label="changePassword ? 'Current Password' : 'Password'" autocomplete="on" persistent-placeholder
hide-details>
</v-text-field>
<v-text-field v-if="changePassword" class="ml-8 mr-8 mb-4" density="compact" variant="outlined"
v-model="newPassword" type="password" label="New Password" autocomplete="on" persistent-placeholder>
</v-text-field>
<v-text-field v-if="!hasLogin || changePassword" class="ml-8 mr-8 mb-4" density="compact" variant="outlined"
v-model="confirmPassword" type="password"
:label="changePassword ? 'Confirm New Password' : 'Confirm Password'" autocomplete="on"
persistent-placeholder>
</v-text-field>
<template v-if="showBasic">
<v-text-field class="ml-8 mr-8 mt-8 mb-8" density="compact" variant="outlined" v-model="username"
autocorrect="off" autocapitalize="off" spellcheck="false" label="User Name" persistent-placeholder
hide-details></v-text-field>
<v-text-field class="ml-8 mr-8 mb-8" density="compact" variant="outlined" v-model="password" type="password"
:label="changePassword ? 'Current Password' : 'Password'" autocomplete="on" persistent-placeholder
hide-details>
</v-text-field>
<v-text-field v-if="changePassword" class="ml-8 mr-8 mb-4" density="compact" variant="outlined"
v-model="newPassword" type="password" label="New Password" autocomplete="on" persistent-placeholder>
</v-text-field>
<v-text-field v-if="!hasLogin || changePassword" class="ml-8 mr-8 mb-4" density="compact" variant="outlined"
v-model="confirmPassword" type="password"
:label="changePassword ? 'Confirm New Password' : 'Confirm Password'" autocomplete="on"
persistent-placeholder>
</v-text-field>
</template>

<div class="pl-8 pr-8 pb-2" style="color: red;" v-if="loginResult">{{ loginResult }}</div>

<div v-if="showBasic && oidcEnabled" class="pl-8 pr-8 pb-2">
<v-divider></v-divider>
</div>

<div v-if="oidcEnabled" class="pl-8 pr-8 pb-4" :class="{ 'pt-6': !showBasic }">
<v-btn block variant="outlined" @click.prevent="signInWithOidc">
{{ showBasic ? 'Or sign in with OIDC' : 'Sign in with OIDC' }}
</v-btn>
</div>

<v-card-actions>
<v-btn :icon="getFaPrefix('fa-home')" v-if="isScryptedCloudHostname()" size="small"
@click.prevent="logoutClient"></v-btn>
<template v-if="hasLogin">
<template v-if="showBasic && hasLogin">
<v-btn size="small" variant="text" v-if="!changePassword" @click="changePassword = true">Change
Password</v-btn>
<v-btn size="small" variant="text" v-else="!changePassword" @click="changePassword = false">Cancel</v-btn>
</template>
<v-spacer></v-spacer>
<v-btn v-if="hasLogin" size="small" type="submit" variant="text" @click.prevent="doLogin">Log In</v-btn>
<v-btn v-else type="submit" size="small" variant="text" @click.prevent="doLogin">Create Account</v-btn>
<template v-if="showBasic">
<v-btn v-if="hasLogin" size="small" type="submit" variant="text" @click.prevent="doLogin">Log In</v-btn>
<v-btn v-else type="submit" size="small" variant="text" @click.prevent="doLogin">Create Account</v-btn>
</template>
</v-card-actions>
</v-card>
</v-form>
</v-dialog>
</template>

<script setup lang="ts">
import { loginScryptedClient, checkScryptedClientLogin } from '@scrypted/client/src/index';
import { ref } from 'vue';
import { getBaseUrl, hasLogin, isScryptedCloudHostname, isSelfHosted, logoutClient, saveSelfHostedCredentials } from '../client';
import { combineBaseUrl, loginScryptedClient, checkScryptedClientLogin } from '@scrypted/client/src/index';
import { computed, ref } from 'vue';
import { authType, getBaseUrl, hasLogin, isScryptedCloudHostname, isSelfHosted, logoutClient, oidcEnabled, saveSelfHostedCredentials } from '../client';
import { windowLocationReload } from '../platform-shims';
import { getFaPrefix } from '../fa-prefix';

Expand All @@ -63,13 +77,23 @@ const newPassword = ref<string>();
const loginResult = ref<string>();
const loginHostname = ref<string>();

const showBasic = computed(() => authType.value !== 'oidc');

const baseUrl = getBaseUrl();
checkScryptedClientLogin({
baseUrl,
})
.then(r => {
loginHostname.value = r.hostname;
if (r.authType) {
authType.value = r.authType;
}
})
.catch(() => { });

function signInWithOidc() {
window.location.href = combineBaseUrl(baseUrl, 'login/oidc');
}

async function doLogin() {
const baseUrl = getBaseUrl();
Expand All @@ -91,7 +115,7 @@ async function doLogin() {
loginResult.value = "New passwords do not match.";
return;
}
change_password = confirmPassword.value;
change_password = newPassword.value;
}

const response = await loginScryptedClient({
Expand All @@ -106,13 +130,13 @@ async function doLogin() {
loginResult.value = response.error;
return;
}

try {
const redirect_uri = new URL(window.location.href).searchParams.get('redirect_uri');
if (redirect_uri) {
if (redirect_uri && new URL(redirect_uri).origin === window.location.origin) {
window.location.href = redirect_uri;
return;
}

}
catch (e) {
}
Expand Down
11 changes: 10 additions & 1 deletion src/components/AppBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,19 @@
<ThemeToggle></ThemeToggle>
<template v-if="isLoggedIn">
<!-- <div class="mr-2">{{ connectedClient?.username }}</div> -->
<v-btn v-if="oidcEnabled && !oidcLinked" @click="linkOidc" size="small" variant="text">
<v-tooltip activator="parent" location="bottom">Link OIDC Account</v-tooltip>
<v-icon>{{ getFaPrefix('fa-link') }}</v-icon>
</v-btn>
<v-btn @click="logoutClient">
<v-icon>{{ getFaPrefix('fa-arrow-right-from-bracket') }}</v-icon>
</v-btn>
</template>
</v-app-bar>
</template>
<script setup lang="ts">
import { connectedClient, isLoggedIn, logoutClient } from '@/common/client';
import { combineBaseUrl } from '@scrypted/client/src/index';
import { getBaseUrl, connectedClient, isLoggedIn, logoutClient, oidcEnabled, oidcLinked } from '@/common/client';
import { getAllDevices } from '@/common/devices';
import { isTouchDevice, isTouchPhone } from '@/common/size';
import { getFaPrefix, hasFixedPhysicalLocation, typeToIcon } from '@/util/device-icons';
Expand Down Expand Up @@ -72,6 +77,10 @@ const search = ref<string>();
const value = ref<SearchResult>();
const router = useRouter();

function linkOidc() {
window.location.href = combineBaseUrl(getBaseUrl(), 'login/oidc/link');
}

const updateAvailable = getServerUpdateMonitor();
const updateAvailableCount = computed(() => updateAvailable.value?.updateAvailable ? 1 : 0);

Expand Down