mirror of
https://github.com/unraid/api.git
synced 2026-01-04 23:50:37 -06:00
feat: integrate cross-domain authentication to api
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[api]
|
||||
version="3.11.0+3f537b97"
|
||||
extraOrigins="https://google.com,https://test.com,http://localhost:4321"
|
||||
version="3.11.0+04f7b636"
|
||||
extraOrigins="https://google.com,https://test.com"
|
||||
[local]
|
||||
[notifier]
|
||||
apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[api]
|
||||
version="3.11.0+3f537b97"
|
||||
extraOrigins="https://google.com,https://test.com,http://localhost:4321"
|
||||
version="3.11.0+04f7b636"
|
||||
extraOrigins="https://google.com,https://test.com"
|
||||
[local]
|
||||
[notifier]
|
||||
apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5"
|
||||
@@ -16,9 +16,9 @@ regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
|
||||
idtoken=""
|
||||
accesstoken=""
|
||||
refreshtoken=""
|
||||
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, http://localhost:4321, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
|
||||
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
|
||||
dynamicRemoteAccessType="DISABLED"
|
||||
[upc]
|
||||
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
|
||||
[connectionStatus]
|
||||
minigraph="CONNECTED"
|
||||
minigraph="ERROR_RETRYING"
|
||||
|
||||
20
api/package-lock.json
generated
20
api/package-lock.json
generated
@@ -75,6 +75,7 @@
|
||||
"openid-client": "^5.6.5",
|
||||
"p-iteration": "^1.1.8",
|
||||
"p-retry": "^4.6.2",
|
||||
"passport-custom": "^1.1.1",
|
||||
"passport-http-header-strategy": "^1.1.0",
|
||||
"pidusage": "^3.0.2",
|
||||
"pino": "^9.1.0",
|
||||
@@ -14492,6 +14493,17 @@
|
||||
"url": "https://github.com/sponsors/jaredhanson"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-custom": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-custom/-/passport-custom-1.1.1.tgz",
|
||||
"integrity": "sha512-/2m7jUGxmCYvoqenLB9UrmkCgPt64h8ZtV+UtuQklZ/Tn1NpKBeOorCYkB/8lMRoiZ5hUrCoMmDtxCS/d38mlg==",
|
||||
"dependencies": {
|
||||
"passport-strategy": "1.x.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-http-header-strategy": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-http-header-strategy/-/passport-http-header-strategy-1.1.0.tgz",
|
||||
@@ -29188,6 +29200,14 @@
|
||||
"utils-merge": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"passport-custom": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-custom/-/passport-custom-1.1.1.tgz",
|
||||
"integrity": "sha512-/2m7jUGxmCYvoqenLB9UrmkCgPt64h8ZtV+UtuQklZ/Tn1NpKBeOorCYkB/8lMRoiZ5hUrCoMmDtxCS/d38mlg==",
|
||||
"requires": {
|
||||
"passport-strategy": "1.x.x"
|
||||
}
|
||||
},
|
||||
"passport-http-header-strategy": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-http-header-strategy/-/passport-http-header-strategy-1.1.0.tgz",
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
"openid-client": "^5.6.5",
|
||||
"p-iteration": "^1.1.8",
|
||||
"p-retry": "^4.6.2",
|
||||
"passport-custom": "^1.1.1",
|
||||
"passport-http-header-strategy": "^1.1.0",
|
||||
"pidusage": "^3.0.2",
|
||||
"pino": "^9.1.0",
|
||||
|
||||
@@ -69,12 +69,12 @@ export const configureFastifyCors =
|
||||
if (typeof cookies === 'object') {
|
||||
service.hasValidAuthCookie(cookies).then((isValid) => {
|
||||
if (isValid) {
|
||||
callback(null, { origin: true });
|
||||
callback(null, { credentials: true, origin: true });
|
||||
} else {
|
||||
callback(null, { origin: isOriginAllowed });
|
||||
callback(null, { credentials: true, origin: isOriginAllowed });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(null, { origin: isOriginAllowed });
|
||||
callback(null, { credentials: true, origin: isOriginAllowed });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,10 +11,11 @@ import { Reflector } from '@nestjs/core';
|
||||
import { GqlExecutionContext, type GqlContextType } from '@nestjs/graphql';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { type Observable } from 'rxjs';
|
||||
import { UserCookieStrategy } from './cookie.strategy';
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlAuthGuard
|
||||
extends AuthGuard([ServerHeaderStrategy.key])
|
||||
extends AuthGuard([ServerHeaderStrategy.key, UserCookieStrategy.key])
|
||||
implements CanActivate
|
||||
{
|
||||
constructor(private readonly reflector: Reflector) {
|
||||
|
||||
@@ -4,12 +4,14 @@ import { UsersModule } from '@app/unraid-api/users/users.module';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { ServerHeaderStrategy } from '@app/unraid-api/auth/header.strategy';
|
||||
import { CookieService, SESSION_COOKIE_CONFIG } from './cookie.service';
|
||||
import { UserCookieStrategy } from './cookie.strategy';
|
||||
|
||||
@Module({
|
||||
imports: [UsersModule, PassportModule],
|
||||
providers: [
|
||||
AuthService,
|
||||
ServerHeaderStrategy,
|
||||
UserCookieStrategy,
|
||||
CookieService,
|
||||
{ provide: SESSION_COOKIE_CONFIG, useValue: CookieService.defaultOpts() },
|
||||
],
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import { type UserAccount } from '@app/graphql/generated/api/types';
|
||||
import { UsersService } from '@app/unraid-api/users/users.service';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { CookieService } from './cookie.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(private usersService: UsersService) {}
|
||||
constructor(private usersService: UsersService, private cookieService: CookieService) {}
|
||||
|
||||
async validateUser(apiKey: string): Promise<UserAccount> {
|
||||
|
||||
const user = this.usersService.findOne(apiKey);
|
||||
if (user) {
|
||||
return user;
|
||||
}
|
||||
throw new UnauthorizedException('Invalid API key');
|
||||
}
|
||||
|
||||
async validateCookies(cookies: object): Promise<UserAccount> {
|
||||
if (await this.cookieService.hasValidAuthCookie(cookies)) {
|
||||
return this.usersService.getSessionUser();
|
||||
}
|
||||
throw new UnauthorizedException('No user session found');
|
||||
}
|
||||
}
|
||||
|
||||
22
api/src/unraid-api/auth/cookie.strategy.ts
Normal file
22
api/src/unraid-api/auth/cookie.strategy.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy } from 'passport-custom';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
const strategyName = 'user-cookie';
|
||||
|
||||
@Injectable()
|
||||
export class UserCookieStrategy extends PassportStrategy(Strategy, strategyName) {
|
||||
static key = strategyName;
|
||||
private readonly logger = new Logger(UserCookieStrategy.name);
|
||||
|
||||
constructor(
|
||||
private readonly authService: AuthService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public validate = async (req: any): Promise<any> => {
|
||||
return this.authService.validateCookies(req.cookies);
|
||||
};
|
||||
}
|
||||
@@ -46,4 +46,19 @@ export class UsersService {
|
||||
findOne(apiKey: string): UserAccount | null {
|
||||
return this.apiKeyToUser(apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a user object representing a session.
|
||||
* Note: Does NOT perform validation.
|
||||
*
|
||||
* @returns a service account that represents the user session (i.e. a webgui user).
|
||||
*/
|
||||
getSessionUser(): UserAccount {
|
||||
return {
|
||||
id: '-1',
|
||||
description: 'UPC service account',
|
||||
name: 'upc',
|
||||
roles: 'upc',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +38,12 @@ watch(notifications, (newVal) => {
|
||||
const fetchType = ref<"UNREAD" | "ARCHIVED">("UNREAD");
|
||||
const setFetchType = (type: "UNREAD" | "ARCHIVED") => (fetchType.value = type);
|
||||
|
||||
const { unraidApiClient } = storeToRefs(useUnraidApiStore());
|
||||
const { unraidApiClient: maybeApi } = storeToRefs(useUnraidApiStore());
|
||||
|
||||
watch(unraidApiClient, async (newVal) => {
|
||||
if (newVal) {
|
||||
const apiResponse = await newVal.query({
|
||||
|
||||
watch(maybeApi, async (apiClient) => {
|
||||
if (apiClient) {
|
||||
const apiResponse = await apiClient.query({
|
||||
query: getNotifications,
|
||||
variables: {
|
||||
filter: {
|
||||
|
||||
@@ -85,11 +85,13 @@ export const useUnraidApiStore = defineStore('unraidApi', () => {
|
||||
// return; // @todo remove
|
||||
unraidApiStatus.value = 'connecting';
|
||||
|
||||
const headers = { 'x-api-key': serverStore.apiKey };
|
||||
// const headers = { 'x-api-key': serverStore.apiKey };
|
||||
const headers = {};
|
||||
|
||||
const httpLink = createHttpLink({
|
||||
uri: httpEndpoint.toString(),
|
||||
headers,
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
const wsLink = new GraphQLWsLink(
|
||||
|
||||
Reference in New Issue
Block a user