Compare commits
No commits in common. "oidc" and "develop" have entirely different histories.
10 changed files with 2 additions and 452 deletions
61
flake.lock
generated
61
flake.lock
generated
|
|
@ -1,61 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1731533236,
|
|
||||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1766651565,
|
|
||||||
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
52
flake.nix
52
flake.nix
|
|
@ -1,52 +0,0 @@
|
||||||
{
|
|
||||||
description = "Development environment for Sharkey";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
|
||||||
let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
buildInputs = with pkgs; [
|
|
||||||
nodejs
|
|
||||||
pnpm_9
|
|
||||||
|
|
||||||
pkg-config
|
|
||||||
python3
|
|
||||||
makeWrapper
|
|
||||||
|
|
||||||
cairo
|
|
||||||
pango
|
|
||||||
pixman
|
|
||||||
vips
|
|
||||||
ffmpeg-headless
|
|
||||||
jemalloc
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
shellHook = ''
|
|
||||||
export NODE_ENV=development
|
|
||||||
export npm_config_nodedir=${pkgs.nodejs}
|
|
||||||
'';
|
|
||||||
|
|
||||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
|
|
||||||
pkgs.ffmpeg-headless
|
|
||||||
pkgs.jemalloc
|
|
||||||
pkgs.stdenv.cc.cc
|
|
||||||
pkgs.cairo
|
|
||||||
pkgs.pango
|
|
||||||
pkgs.pixman
|
|
||||||
pkgs.vips
|
|
||||||
];
|
|
||||||
|
|
||||||
PKG_CONFIG_PATH = "${pkgs.cairo}/lib/pkgconfig:${pkgs.pango}/lib/pkgconfig:${pkgs.pixman}/lib/pkgconfig:${pkgs.vips}/lib/pkgconfig";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -138,7 +138,6 @@
|
||||||
"ip-cidr": "4.0.2",
|
"ip-cidr": "4.0.2",
|
||||||
"ipaddr.js": "2.2.0",
|
"ipaddr.js": "2.2.0",
|
||||||
"is-svg": "6.1.0",
|
"is-svg": "6.1.0",
|
||||||
"jose": "^6.1.3",
|
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jsonld": "8.3.3",
|
"jsonld": "8.3.3",
|
||||||
|
|
|
||||||
|
|
@ -161,17 +161,6 @@ type Source = {
|
||||||
customHtml?: {
|
customHtml?: {
|
||||||
head?: string;
|
head?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
oidc: {
|
|
||||||
name: string;
|
|
||||||
authEndpoint: string;
|
|
||||||
tokenEndpoint: string;
|
|
||||||
clientId: string;
|
|
||||||
clientSecret: string;
|
|
||||||
scope: string[];
|
|
||||||
autoRegister: boolean;
|
|
||||||
usernameClaim: string;
|
|
||||||
} | undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PrivateNetworkSource = string | { network?: string, ports?: number[] };
|
export type PrivateNetworkSource = string | { network?: string, ports?: number[] };
|
||||||
|
|
@ -350,17 +339,6 @@ export type Config = {
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
|
||||||
pidFile: string;
|
pidFile: string;
|
||||||
|
|
||||||
oidc: {
|
|
||||||
name: string;
|
|
||||||
authEndpoint: string;
|
|
||||||
tokenEndpoint: string;
|
|
||||||
clientId: string;
|
|
||||||
clientSecret: string;
|
|
||||||
scope: string[];
|
|
||||||
autoRegister: boolean;
|
|
||||||
usernameClaim: string;
|
|
||||||
} | undefined;
|
|
||||||
filePermissionBits?: string;
|
filePermissionBits?: string;
|
||||||
|
|
||||||
activityLogging: {
|
activityLogging: {
|
||||||
|
|
@ -543,7 +521,6 @@ export function loadConfig(loggerService: LoggerService): Config {
|
||||||
customHtml: {
|
customHtml: {
|
||||||
head: config.customHtml?.head ?? '',
|
head: config.customHtml?.head ?? '',
|
||||||
},
|
},
|
||||||
oidc: config.oidc,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ import { ChatRoomChannelService } from './api/stream/channels/chat-room.js';
|
||||||
import { ReversiChannelService } from './api/stream/channels/reversi.js';
|
import { ReversiChannelService } from './api/stream/channels/reversi.js';
|
||||||
import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js';
|
import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js';
|
||||||
import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.js';
|
import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.js';
|
||||||
import { OidcApiService } from './api/OidcApiService.js';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -129,7 +128,6 @@ import { OidcApiService } from './api/OidcApiService.js';
|
||||||
ApiStatusMastodon,
|
ApiStatusMastodon,
|
||||||
ApiTimelineMastodon,
|
ApiTimelineMastodon,
|
||||||
ServerUtilityService,
|
ServerUtilityService,
|
||||||
OidcApiService,
|
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ServerService,
|
ServerService,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import { SignupApiService } from './SignupApiService.js';
|
||||||
import { SigninApiService } from './SigninApiService.js';
|
import { SigninApiService } from './SigninApiService.js';
|
||||||
import { SigninWithPasskeyApiService } from './SigninWithPasskeyApiService.js';
|
import { SigninWithPasskeyApiService } from './SigninWithPasskeyApiService.js';
|
||||||
import { CacheService } from '@/core/CacheService.js';
|
import { CacheService } from '@/core/CacheService.js';
|
||||||
import { OidcApiService } from './OidcApiService.js';
|
|
||||||
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -45,7 +44,6 @@ export class ApiServerService {
|
||||||
private signinApiService: SigninApiService,
|
private signinApiService: SigninApiService,
|
||||||
private signinWithPasskeyApiService: SigninWithPasskeyApiService,
|
private signinWithPasskeyApiService: SigninWithPasskeyApiService,
|
||||||
private cacheService: CacheService,
|
private cacheService: CacheService,
|
||||||
private oidcApiService: OidcApiService,
|
|
||||||
) {
|
) {
|
||||||
//this.createServer = this.createServer.bind(this);
|
//this.createServer = this.createServer.bind(this);
|
||||||
}
|
}
|
||||||
|
|
@ -212,14 +210,6 @@ export class ApiServerService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get('/oidc/meta', (request, reply) => this.oidcApiService.meta(request, reply));
|
|
||||||
fastify.get<{
|
|
||||||
Querystring: {
|
|
||||||
code?: string;
|
|
||||||
error?: string;
|
|
||||||
};
|
|
||||||
}>('/oidc/callback', (request, reply) => this.oidcApiService.callback(request, reply));
|
|
||||||
|
|
||||||
// Make sure any unknown path under /api returns HTTP 404 Not Found,
|
// Make sure any unknown path under /api returns HTTP 404 Not Found,
|
||||||
// because otherwise ClientServerService will return the base client HTML
|
// because otherwise ClientServerService will return the base client HTML
|
||||||
// page with HTTP 200.
|
// page with HTTP 200.
|
||||||
|
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: skye and other Sharkey contributors
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import * as jose from 'jose';
|
|
||||||
import { IsNull } from 'typeorm';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import type {
|
|
||||||
UsedUsernamesRepository,
|
|
||||||
UsersRepository,
|
|
||||||
} from '@/models/_.js';
|
|
||||||
import type { Config } from '@/config.js';
|
|
||||||
import { getIpHash } from '@/misc/get-ip-hash.js';
|
|
||||||
import type { MiLocalUser } from '@/models/User.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
|
||||||
import { FastifyReplyError } from '@/misc/fastify-reply-error.js';
|
|
||||||
import { SignupService } from '@/core/SignupService.js';
|
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
|
||||||
import { SkRateLimiterService } from '@/server/SkRateLimiterService.js';
|
|
||||||
import { SigninService } from './SigninService.js';
|
|
||||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class OidcApiService {
|
|
||||||
constructor(
|
|
||||||
@Inject(DI.config)
|
|
||||||
private config: Config,
|
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
|
||||||
private usersRepository: UsersRepository,
|
|
||||||
|
|
||||||
@Inject(DI.usedUsernamesRepository)
|
|
||||||
private usedUsernamesRepository: UsedUsernamesRepository,
|
|
||||||
|
|
||||||
private rateLimiterService: SkRateLimiterService,
|
|
||||||
private signinService: SigninService,
|
|
||||||
private signupService: SignupService,
|
|
||||||
private metaService: MetaService,
|
|
||||||
private httpRequestService: HttpRequestService,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async meta(
|
|
||||||
_request: FastifyRequest,
|
|
||||||
_reply: FastifyReply,
|
|
||||||
) {
|
|
||||||
if (this.config.oidc === undefined) {
|
|
||||||
return {
|
|
||||||
enabled: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const url = new URL(this.config.oidc.authEndpoint);
|
|
||||||
url.searchParams.append('client_id', this.config.oidc.clientId);
|
|
||||||
url.searchParams.append('redirect_uri', `${this.config.apiUrl}/oidc/callback`);
|
|
||||||
url.searchParams.append('response_type', 'code');
|
|
||||||
url.searchParams.append('scope', this.config.oidc.scope.join(' '));
|
|
||||||
return {
|
|
||||||
enabled: true,
|
|
||||||
name: this.config.oidc.name,
|
|
||||||
url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@bindThis
|
|
||||||
public async callback(
|
|
||||||
request: FastifyRequest<{
|
|
||||||
Querystring: {
|
|
||||||
code?: string;
|
|
||||||
error?: string;
|
|
||||||
};
|
|
||||||
}>,
|
|
||||||
reply: FastifyReply,
|
|
||||||
) {
|
|
||||||
reply.header('Access-Control-Allow-Origin', this.config.url);
|
|
||||||
reply.header('Access-Control-Allow-Credentials', 'true');
|
|
||||||
|
|
||||||
const instance = await this.metaService.fetch(true);
|
|
||||||
|
|
||||||
const query = request.query;
|
|
||||||
|
|
||||||
function error(status: number, error: { id: string }) {
|
|
||||||
reply.code(status);
|
|
||||||
return { error };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query.error !== undefined) {
|
|
||||||
return error(403, {
|
|
||||||
id: 'bf721f0c-ed11-4c94-bc0a-84aaf8b7431a',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query.code === undefined) {
|
|
||||||
return error(400, {
|
|
||||||
id: '797c0ae2-2eb9-402d-a2d6-090b5f3f3686',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.oidc === undefined) {
|
|
||||||
return error(403, {
|
|
||||||
id: '726c5012-eec4-49c8-b613-74fb23c2bdce',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// not more than 1 attempt per second and not more than 10 attempts per hour
|
|
||||||
await this.rateLimiterService.limit({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(request.ip));
|
|
||||||
} catch (err) {
|
|
||||||
reply.code(429);
|
|
||||||
return {
|
|
||||||
error: {
|
|
||||||
message: 'Too many failed attempts to sign in. Try again later.',
|
|
||||||
code: 'TOO_MANY_AUTHENTICATION_FAILURES',
|
|
||||||
id: '22d05606-fbcf-421a-a2db-b32610dcfd1b',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenResponse: { id_token: string } | { error: string } = await (await this.httpRequestService.send(this.config.oidc.tokenEndpoint, {
|
|
||||||
body: new URLSearchParams({
|
|
||||||
code: query.code,
|
|
||||||
client_id: this.config.oidc.clientId,
|
|
||||||
client_secret: this.config.oidc.clientSecret,
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
redirect_uri: `${this.config.apiUrl}/oidc/callback`,
|
|
||||||
}).toString(),
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
},
|
|
||||||
method: 'POST',
|
|
||||||
})).json() as { id_token: string } | { error: string };
|
|
||||||
|
|
||||||
if ('error' in tokenResponse) {
|
|
||||||
return error(403, {
|
|
||||||
id: 'afae6924-7744-4efb-aed1-cfda2ddd8c43',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = jose.decodeJwt<Record<string, string>>(tokenResponse.id_token);
|
|
||||||
|
|
||||||
const username = token[this.config.oidc.usernameClaim];
|
|
||||||
|
|
||||||
if (username === undefined) {
|
|
||||||
return error(400, {
|
|
||||||
id: '3b02c880-cd46-4886-9cda-325529340226',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch user
|
|
||||||
let user = await this.usersRepository.findOneBy({
|
|
||||||
usernameLower: username.toLowerCase(),
|
|
||||||
host: IsNull(),
|
|
||||||
}) as MiLocalUser;
|
|
||||||
|
|
||||||
if (user == null) {
|
|
||||||
if (this.config.oidc.autoRegister) {
|
|
||||||
try {
|
|
||||||
if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
|
|
||||||
throw new FastifyReplyError(400, 'USED_USERNAME');
|
|
||||||
}
|
|
||||||
const { account } = await this.signupService.signup({
|
|
||||||
username,
|
|
||||||
});
|
|
||||||
user = account as MiLocalUser;
|
|
||||||
} catch (err) {
|
|
||||||
throw new FastifyReplyError(400, typeof err === 'string' ? err : (err as Error).toString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return error(404, {
|
|
||||||
id: '6cc579cc-885d-43d8-95c2-b8c7fc963280',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.isSuspended) {
|
|
||||||
return error(403, {
|
|
||||||
id: 'e03a5f46-d309-4865-9b69-56282d94e1eb',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.approved && instance.approvalRequiredForSignup) {
|
|
||||||
reply.code(403);
|
|
||||||
return {
|
|
||||||
error: {
|
|
||||||
message: 'The account has not been approved by an admin yet. Try again later.',
|
|
||||||
code: 'NOT_APPROVED',
|
|
||||||
id: '22d05606-fbcf-421a-a2db-b32241faft1b',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!instance.approvalRequiredForSignup && !user.approved) this.usersRepository.update(user.id, { approved: true });
|
|
||||||
|
|
||||||
const resp = this.signinService.signin(request, reply, user);
|
|
||||||
reply.type('text/html');
|
|
||||||
return `
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<title>Please wait...</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
Please wait...
|
|
||||||
<script>
|
|
||||||
window.opener.postMessage({
|
|
||||||
type: 'login-ok',
|
|
||||||
data: ${JSON.stringify(resp)}
|
|
||||||
}, '${this.config.url}');
|
|
||||||
window.close();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -30,8 +30,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- username入力 -->
|
<!-- username入力 -->
|
||||||
<form class="_gaps_s" @submit.prevent="emit('usernameSubmitted', username)">
|
<form class="_gaps_s" @submit.prevent="emit('usernameSubmitted', username)">
|
||||||
<MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username webauthn" autofocus required data-cy-signin-username>
|
<MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username webauthn" autofocus required data-cy-signin-username>
|
||||||
|
|
@ -50,29 +48,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<i class="ti ti-device-usb" style="font-size: medium;"></i>{{ i18n.ts.signinWithPasskey }}
|
<i class="ti ti-device-usb" style="font-size: medium;"></i>{{ i18n.ts.signinWithPasskey }}
|
||||||
</MkButton>
|
</MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- OIDC login -->
|
|
||||||
<div :class="$style.orHr">
|
|
||||||
<p :class="$style.orMsg">{{ i18n.ts.or }}</p>
|
|
||||||
</div>
|
|
||||||
<div v-if="oidcLogin">
|
|
||||||
<MkButton type="button" large primary rounded style="margin: 0 auto;" @click="oidcPopup">
|
|
||||||
Sign in with {{ oidcName }}
|
|
||||||
</MkButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { toUnicode } from 'punycode.js';
|
import { toUnicode } from 'punycode.js';
|
||||||
|
|
||||||
import { query, extractDomain } from '@@/js/url.js';
|
import { query, extractDomain } from '@@/js/url.js';
|
||||||
import { host as configHost } from '@@/js/config.js';
|
import { host as configHost } from '@@/js/config.js';
|
||||||
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
||||||
import { misskeyApiGet } from '@/utility/misskey-api.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
|
|
@ -91,52 +77,12 @@ const props = withDefaults(defineProps<{
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'usernameSubmitted', v: string): void;
|
(ev: 'usernameSubmitted', v: string): void;
|
||||||
(ev: 'passkeyClick', v: MouseEvent): void;
|
(ev: 'passkeyClick', v: MouseEvent): void;
|
||||||
(ev: 'oidcLogin', v: any): void;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const host = toUnicode(configHost);
|
const host = toUnicode(configHost);
|
||||||
|
|
||||||
const username = ref('');
|
const username = ref('');
|
||||||
|
|
||||||
const oidcName = ref('OpenID Connect');
|
|
||||||
const oidcLogin = ref(false);
|
|
||||||
|
|
||||||
let oidcUrl: string | undefined;
|
|
||||||
|
|
||||||
misskeyApiGet('oidc/meta').then((resp: {
|
|
||||||
enabled: false
|
|
||||||
} | {
|
|
||||||
enabled: true,
|
|
||||||
name: string,
|
|
||||||
url: string,
|
|
||||||
}) => {
|
|
||||||
oidcLogin.value = resp.enabled;
|
|
||||||
if (resp.enabled) {
|
|
||||||
oidcName.value = resp.name;
|
|
||||||
oidcUrl = resp.url;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function oidcPopup() {
|
|
||||||
window.open(oidcUrl, undefined, 'popup');
|
|
||||||
}
|
|
||||||
|
|
||||||
function oidcEvent(event) {
|
|
||||||
const data = event.data;
|
|
||||||
if (typeof data === 'object' && 'type' in data && data.type === 'login-ok') {
|
|
||||||
const res = (data as { type: 'login-ok', data: unknown }).data;
|
|
||||||
emit('oidcLogin', res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener('message', oidcEvent);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('message', oidcEvent);
|
|
||||||
});
|
|
||||||
|
|
||||||
//#region Open on remote
|
//#region Open on remote
|
||||||
function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
|
function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
|
||||||
switch (options.type) {
|
switch (options.type) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
@usernameSubmitted="onUsernameSubmitted"
|
@usernameSubmitted="onUsernameSubmitted"
|
||||||
@passkeyClick="onPasskeyLogin"
|
@passkeyClick="onPasskeyLogin"
|
||||||
@oidcLogin="onOidcLogin"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 2. パスワード入力 -->
|
<!-- 2. パスワード入力 -->
|
||||||
|
|
@ -57,13 +56,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
@done="onPasskeyDone"
|
@done="onPasskeyDone"
|
||||||
@useTotp="onUseTotp"
|
@useTotp="onUseTotp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<div v-if="waiting" :class="$style.waitingRoot">
|
<div v-if="waiting" :class="$style.waitingRoot">
|
||||||
<MkLoading/>
|
<MkLoading/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -74,7 +70,7 @@ import { supported as webAuthnSupported, parseRequestOptionsFromJSON } from '@gi
|
||||||
import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
|
import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
|
||||||
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
|
||||||
import type { PwResponse } from '@/components/MkSignin.password.vue';
|
import type { PwResponse } from '@/components/MkSignin.password.vue';
|
||||||
import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
import { showSuspendedDialog } from '@/utility/show-suspended-dialog.js';
|
import { showSuspendedDialog } from '@/utility/show-suspended-dialog.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { showSystemAccountDialog } from '@/utility/show-system-account-dialog.js';
|
import { showSystemAccountDialog } from '@/utility/show-system-account-dialog.js';
|
||||||
|
|
@ -109,13 +105,6 @@ const needCaptcha = ref(false);
|
||||||
const userInfo = ref<null | Misskey.entities.UserDetailed>(null);
|
const userInfo = ref<null | Misskey.entities.UserDetailed>(null);
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
|
|
||||||
//#region OIDC
|
|
||||||
function onOidcLogin(res: any): void {
|
|
||||||
emit('login', res);
|
|
||||||
onLoginSucceeded(res);
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Passkey Passwordless
|
//#region Passkey Passwordless
|
||||||
const credentialRequest = shallowRef<CredentialRequestOptions | null>(null);
|
const credentialRequest = shallowRef<CredentialRequestOptions | null>(null);
|
||||||
const passkeyContext = ref('');
|
const passkeyContext = ref('');
|
||||||
|
|
|
||||||
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
|
|
@ -258,9 +258,6 @@ importers:
|
||||||
is-svg:
|
is-svg:
|
||||||
specifier: 6.1.0
|
specifier: 6.1.0
|
||||||
version: 6.1.0
|
version: 6.1.0
|
||||||
jose:
|
|
||||||
specifier: ^6.1.3
|
|
||||||
version: 6.1.3
|
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: 4.1.0
|
specifier: 4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
|
|
@ -7536,9 +7533,6 @@ packages:
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
jose@5.10.0:
|
|
||||||
resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==}
|
|
||||||
|
|
||||||
js-cookie@3.0.5:
|
js-cookie@3.0.5:
|
||||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
@ -14381,15 +14375,7 @@ snapshots:
|
||||||
|
|
||||||
'@transfem-org/summaly@5.2.3':
|
'@transfem-org/summaly@5.2.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
<<<<<<< HEAD
|
|
||||||
cheerio: 1.1.2
|
cheerio: 1.1.2
|
||||||
=======
|
|
||||||
'@twemoji/parser': 15.0.0
|
|
||||||
|
|
||||||
'@transfem-org/summaly@5.2.1':
|
|
||||||
dependencies:
|
|
||||||
cheerio: 1.0.0
|
|
||||||
>>>>>>> cd7330e1a2 (add OIDC support)
|
|
||||||
escape-regexp: 0.0.1
|
escape-regexp: 0.0.1
|
||||||
got: 14.4.9
|
got: 14.4.9
|
||||||
html-entities: 2.6.0
|
html-entities: 2.6.0
|
||||||
|
|
@ -18529,7 +18515,6 @@ snapshots:
|
||||||
'@sideway/formula': 3.0.1
|
'@sideway/formula': 3.0.1
|
||||||
'@sideway/pinpoint': 2.0.0
|
'@sideway/pinpoint': 2.0.0
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
joi@18.0.1:
|
joi@18.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hapi/address': 5.1.1
|
'@hapi/address': 5.1.1
|
||||||
|
|
@ -18541,11 +18526,6 @@ snapshots:
|
||||||
'@standard-schema/spec': 1.0.0
|
'@standard-schema/spec': 1.0.0
|
||||||
|
|
||||||
js-beautify@1.15.4:
|
js-beautify@1.15.4:
|
||||||
=======
|
|
||||||
jose@5.10.0: {}
|
|
||||||
|
|
||||||
js-beautify@1.14.9:
|
|
||||||
>>>>>>> cd7330e1a2 (add OIDC support)
|
|
||||||
dependencies:
|
dependencies:
|
||||||
config-chain: 1.1.13
|
config-chain: 1.1.13
|
||||||
editorconfig: 1.0.4
|
editorconfig: 1.0.4
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue