Merge pull request #2372 from tidusjar/feature/PlexOAuthFix

Feature/plex o auth fix
pull/2383/head
Jamie 7 years ago committed by GitHub
commit 22edcf56d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -22,8 +22,7 @@ namespace Ombi.Api.Plex
Task<PlexFriends> GetUsers(string authToken); Task<PlexFriends> GetUsers(string authToken);
Task<PlexAccount> GetAccount(string authToken); Task<PlexAccount> GetAccount(string authToken);
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId); Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
Task<OAuthPin> CreatePin();
Task<OAuthPin> GetPin(int pinId); Task<OAuthPin> GetPin(int pinId);
Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard); Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard);
} }
} }

@ -16,14 +16,16 @@ namespace Ombi.Api.Plex
{ {
public class PlexApi : IPlexApi public class PlexApi : IPlexApi
{ {
public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings) public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings, ISettingsService<PlexSettings> p)
{ {
Api = api; Api = api;
_custom = settings; _custom = settings;
_plexSettings = p;
} }
private IApi Api { get; } private IApi Api { get; }
private readonly ISettingsService<CustomizationSettings> _custom; private readonly ISettingsService<CustomizationSettings> _custom;
private readonly ISettingsService<PlexSettings> _plexSettings;
private string _app; private string _app;
private string ApplicationName private string ApplicationName
@ -69,7 +71,7 @@ namespace Ombi.Api.Plex
}; };
var request = new Request(SignInUri, string.Empty, HttpMethod.Post); var request = new Request(SignInUri, string.Empty, HttpMethod.Post);
AddHeaders(request); await AddHeaders(request);
request.AddJsonBody(userModel); request.AddJsonBody(userModel);
var obj = await Api.Request<PlexAuthentication>(request); var obj = await Api.Request<PlexAuthentication>(request);
@ -80,14 +82,14 @@ namespace Ombi.Api.Plex
public async Task<PlexStatus> GetStatus(string authToken, string uri) public async Task<PlexStatus> GetStatus(string authToken, string uri)
{ {
var request = new Request(uri, string.Empty, HttpMethod.Get); var request = new Request(uri, string.Empty, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexStatus>(request); return await Api.Request<PlexStatus>(request);
} }
public async Task<PlexAccount> GetAccount(string authToken) public async Task<PlexAccount> GetAccount(string authToken)
{ {
var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get); var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexAccount>(request); return await Api.Request<PlexAccount>(request);
} }
@ -95,7 +97,7 @@ namespace Ombi.Api.Plex
{ {
var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml); var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexServer>(request); return await Api.Request<PlexServer>(request);
} }
@ -103,14 +105,14 @@ namespace Ombi.Api.Plex
public async Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost) public async Task<PlexContainer> GetLibrarySections(string authToken, string plexFullHost)
{ {
var request = new Request("library/sections", plexFullHost, HttpMethod.Get); var request = new Request("library/sections", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexContainer>(request); return await Api.Request<PlexContainer>(request);
} }
public async Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId) public async Task<PlexContainer> GetLibrary(string authToken, string plexFullHost, string libraryId)
{ {
var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get); var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexContainer>(request); return await Api.Request<PlexContainer>(request);
} }
@ -128,21 +130,21 @@ namespace Ombi.Api.Plex
public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey) public async Task<PlexMetadata> GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey)
{ {
var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get); var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId) public async Task<PlexMetadata> GetMetadata(string authToken, string plexFullHost, int itemId)
{ {
var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get); var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey) public async Task<PlexMetadata> GetSeasons(string authToken, string plexFullHost, int ratingKey)
{ {
var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get); var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
@ -161,9 +163,9 @@ namespace Ombi.Api.Plex
request.AddQueryString("type", "4"); request.AddQueryString("type", "4");
AddLimitHeaders(request, start, retCount); AddLimitHeaders(request, start, retCount);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexContainer>(request); return await Api.Request<PlexContainer>(request);
} }
/// <summary> /// <summary>
@ -174,8 +176,8 @@ namespace Ombi.Api.Plex
/// <returns></returns> /// <returns></returns>
public async Task<PlexFriends> GetUsers(string authToken) public async Task<PlexFriends> GetUsers(string authToken)
{ {
var request = new Request(string.Empty,FriendsUri, HttpMethod.Get, ContentType.Xml); var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
return await Api.Request<PlexFriends>(request); return await Api.Request<PlexFriends>(request);
} }
@ -183,43 +185,40 @@ namespace Ombi.Api.Plex
public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId) public async Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId)
{ {
var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get); var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get);
AddHeaders(request, authToken); await AddHeaders(request, authToken);
AddLimitHeaders(request, 0, 50); AddLimitHeaders(request, 0, 50);
return await Api.Request<PlexMetadata>(request); return await Api.Request<PlexMetadata>(request);
} }
public async Task<OAuthPin> CreatePin()
{
var request = new Request($"api/v2/pins", "https://plex.tv/", HttpMethod.Post);
request.AddQueryString("strong", "true");
AddHeaders(request);
return await Api.Request<OAuthPin>(request);
}
public async Task<OAuthPin> GetPin(int pinId) public async Task<OAuthPin> GetPin(int pinId)
{ {
var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get); var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get);
AddHeaders(request); await AddHeaders(request);
return await Api.Request<OAuthPin>(request); return await Api.Request<OAuthPin>(request);
} }
public Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard) public async Task<Uri> GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard)
{ {
var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get); var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get);
AddHeaders(request); await AddHeaders(request);
var forwardUrl = wizard var forwardUrl = wizard
? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get) ? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get)
: new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get); : new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get);
request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString());
request.AddQueryString("pinID", pinId.ToString()); request.AddQueryString("pinID", pinId.ToString());
request.AddQueryString("code", code); request.AddQueryString("code", code);
request.AddQueryString("context[device][product]", "Ombi"); request.AddQueryString("context[device][product]", ApplicationName);
request.AddQueryString("context[device][environment]", "bundled"); request.AddQueryString("context[device][environment]", "bundled");
request.AddQueryString("clientID", $"OmbiV3"); request.AddQueryString("context[device][layout]", "desktop");
request.AddQueryString("context[device][platform]", "Web");
request.AddQueryString("context[device][device]", "Ombi (Web)");
var s = await GetSettings();
await CheckInstallId(s);
request.AddQueryString("clientID", s.InstallId.ToString("N"));
if (request.FullUri.Fragment.Equals("#")) if (request.FullUri.Fragment.Equals("#"))
{ {
@ -238,21 +237,25 @@ namespace Ombi.Api.Plex
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="authToken"></param> /// <param name="authToken"></param>
private void AddHeaders(Request request, string authToken) private async Task AddHeaders(Request request, string authToken)
{ {
request.AddHeader("X-Plex-Token", authToken); request.AddHeader("X-Plex-Token", authToken);
AddHeaders(request); await AddHeaders(request);
} }
/// <summary> /// <summary>
/// Adds the main required headers to the Plex Request /// Adds the main required headers to the Plex Request
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
private void AddHeaders(Request request) private async Task AddHeaders(Request request)
{ {
request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3"); var s = await GetSettings();
await CheckInstallId(s);
request.AddHeader("X-Plex-Client-Identifier", s.InstallId.ToString("N"));
request.AddHeader("X-Plex-Product", ApplicationName); request.AddHeader("X-Plex-Product", ApplicationName);
request.AddHeader("X-Plex-Version", "3"); request.AddHeader("X-Plex-Version", "3");
request.AddHeader("X-Plex-Device", "Ombi (Web)");
request.AddHeader("X-Plex-Platform", "Web");
request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml"); request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml");
request.AddHeader("Accept", "application/json"); request.AddHeader("Accept", "application/json");
} }
@ -262,5 +265,19 @@ namespace Ombi.Api.Plex
request.AddHeader("X-Plex-Container-Start", from.ToString()); request.AddHeader("X-Plex-Container-Start", from.ToString());
request.AddHeader("X-Plex-Container-Size", to.ToString()); request.AddHeader("X-Plex-Container-Size", to.ToString());
} }
private async Task CheckInstallId(PlexSettings s)
{
if (s.InstallId == null || s.InstallId == Guid.Empty)
{
s.InstallId = Guid.NewGuid();
await _plexSettings.SaveSettingsAsync(s);
}
}
private PlexSettings _settings;
private async Task<PlexSettings> GetSettings()
{
return _settings ?? (_settings = await _plexSettings.GetSettingsAsync());
}
} }
} }

@ -20,12 +20,6 @@ namespace Ombi.Core.Authentication
private readonly IPlexApi _api; private readonly IPlexApi _api;
private readonly ISettingsService<CustomizationSettings> _customizationSettingsService; private readonly ISettingsService<CustomizationSettings> _customizationSettingsService;
public async Task<OAuthPin> RequestPin()
{
var pin = await _api.CreatePin();
return pin;
}
public async Task<string> GetAccessTokenFromPin(int pinId) public async Task<string> GetAccessTokenFromPin(int pinId)
{ {
var pin = await _api.GetPin(pinId); var pin = await _api.GetPin(pinId);
@ -58,14 +52,14 @@ namespace Ombi.Core.Authentication
public async Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null) public async Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null)
{ {
var settings = await _customizationSettingsService.GetSettingsAsync(); var settings = await _customizationSettingsService.GetSettingsAsync();
var url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl, false); var url = await _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl, false);
return url; return url;
} }
public Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress) public async Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress)
{ {
var url = _api.GetOAuthUrl(pinId, code, websiteAddress, true); var url = await _api.GetOAuthUrl(pinId, code, websiteAddress, true);
return url; return url;
} }
} }

@ -1,16 +1,14 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Api.Plex.Models; using Ombi.Api.Plex.Models;
using Ombi.Api.Plex.Models.OAuth;
namespace Ombi.Core.Authentication namespace Ombi.Core.Authentication
{ {
public interface IPlexOAuthManager public interface IPlexOAuthManager
{ {
Task<string> GetAccessTokenFromPin(int pinId); Task<string> GetAccessTokenFromPin(int pinId);
Task<OAuthPin> RequestPin();
Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null); Task<Uri> GetOAuthUrl(int pinId, string code, string websiteAddress = null);
Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress); Task<Uri> GetWizardOAuthUrl(int pinId, string code, string websiteAddress);
Task<PlexAccount> GetAccount(string accessToken); Task<PlexAccount> GetAccount(string accessToken);
} }
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using Ombi.Settings.Settings.Models.External; using Ombi.Settings.Settings.Models.External;
namespace Ombi.Core.Settings.Models.External namespace Ombi.Core.Settings.Models.External
@ -6,6 +7,10 @@ namespace Ombi.Core.Settings.Models.External
public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings
{ {
public bool Enable { get; set; } public bool Enable { get; set; }
/// <summary>
/// This is the ClientId for OAuth
/// </summary>
public Guid InstallId { get; set; }
public List<PlexServers> Servers { get; set; } public List<PlexServers> Servers { get; set; }
} }

@ -36,7 +36,7 @@ import { ImageService } from "./services";
import { LandingPageService } from "./services"; import { LandingPageService } from "./services";
import { NotificationService } from "./services"; import { NotificationService } from "./services";
import { SettingsService } from "./services"; import { SettingsService } from "./services";
import { IssuesService, JobService, StatusService } from "./services"; import { IssuesService, JobService, PlexTvService, StatusService } from "./services";
const routes: Routes = [ const routes: Routes = [
{ path: "*", component: PageNotFoundComponent }, { path: "*", component: PageNotFoundComponent },
@ -133,6 +133,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
CookieService, CookieService,
JobService, JobService,
IssuesService, IssuesService,
PlexTvService,
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })

@ -1,8 +1,11 @@
export interface IUserLogin { import { IPlexPin } from "../interfaces";
export interface IUserLogin {
username: string; username: string;
password: string; password: string;
rememberMe: boolean; rememberMe: boolean;
usePlexOAuth: boolean; usePlexOAuth: boolean;
plexTvPin: IPlexPin;
} }
export interface ILocalUser { export interface ILocalUser {

@ -2,6 +2,16 @@
user: IPlexUser; user: IPlexUser;
} }
export interface IPlexPin {
id: number;
code: string;
}
export interface IPlexOAuthViewModel {
wizard: boolean;
pin: IPlexPin;
}
export interface IPlexOAuthAccessToken { export interface IPlexOAuthAccessToken {
accessToken: string; accessToken: string;
} }

@ -6,7 +6,7 @@ import { TranslateService } from "@ngx-translate/core";
import { PlatformLocation } from "@angular/common"; import { PlatformLocation } from "@angular/common";
import { AuthService } from "../auth/auth.service"; import { AuthService } from "../auth/auth.service";
import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces"; import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces";
import { NotificationService } from "../services"; import { NotificationService, PlexTvService } from "../services";
import { SettingsService } from "../services"; import { SettingsService } from "../services";
import { StatusService } from "../services"; import { StatusService } from "../services";
@ -40,13 +40,14 @@ export class LoginComponent implements OnDestroy, OnInit {
} }
private timer: any; private timer: any;
private clientId: string;
private errorBody: string; private errorBody: string;
private errorValidation: string; private errorValidation: string;
constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService, constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService,
private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer, private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer,
private route: ActivatedRoute, private location: PlatformLocation, private readonly translate: TranslateService) { private route: ActivatedRoute, private location: PlatformLocation, private translate: TranslateService, private plexTv: PlexTvService) {
this.route.params this.route.params
.subscribe((params: any) => { .subscribe((params: any) => {
this.landingFlag = params.landing; this.landingFlag = params.landing;
@ -78,6 +79,7 @@ export class LoginComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x); this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x);
this.settingsService.getClientId().subscribe(x => this.clientId = x);
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
this.images.getRandomBackground().subscribe(x => { this.images.getRandomBackground().subscribe(x => {
this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")"); this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")");
@ -101,7 +103,7 @@ export class LoginComponent implements OnDestroy, OnInit {
return; return;
} }
const value = form.value; const value = form.value;
const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false }; const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false, plexTvPin: { id: 0, code: ""} };
this.authService.requiresPassword(user).subscribe(x => { this.authService.requiresPassword(user).subscribe(x => {
if(x && this.authenticationSettings.allowNoPassword) { if(x && this.authenticationSettings.allowNoPassword) {
// Looks like this user requires a password // Looks like this user requires a password
@ -123,14 +125,17 @@ export class LoginComponent implements OnDestroy, OnInit {
} }
public oauth() { public oauth() {
this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => { this.plexTv.GetPin(this.clientId, this.appName).subscribe(pin => {
if (window.frameElement) {
// in frame this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:"", plexTvPin: pin}).subscribe(x => {
window.open(x.url, "_blank"); if (window.frameElement) {
} else { // in frame
// not in frame window.open(x.url, "_blank");
window.location.href = x.url; } else {
} // not in frame
window.location.href = x.url;
}
});
}); });
} }

@ -5,3 +5,4 @@ export * from "./radarr.service";
export * from "./sonarr.service"; export * from "./sonarr.service";
export * from "./tester.service"; export * from "./tester.service";
export * from "./plexoauth.service"; export * from "./plexoauth.service";
export * from "./plextv.service";

@ -6,7 +6,7 @@ import { Observable } from "rxjs/Rx";
import { ServiceHelpers } from "../service.helpers"; import { ServiceHelpers } from "../service.helpers";
import { IPlexAuthentication, IPlexLibResponse, IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces"; import { IPlexAuthentication, IPlexLibResponse, IPlexOAuthViewModel,IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces";
@Injectable() @Injectable()
export class PlexService extends ServiceHelpers { export class PlexService extends ServiceHelpers {
@ -30,7 +30,7 @@ export class PlexService extends ServiceHelpers {
return this.http.get<IUsersModel[]>(`${this.url}Friends`, {headers: this.headers}); return this.http.get<IUsersModel[]>(`${this.url}Friends`, {headers: this.headers});
} }
public oAuth(wizard: boolean): Observable<any> { public oAuth(wizard: IPlexOAuthViewModel): Observable<any> {
return this.http.get<any>(`${this.url}oauth/${wizard}`, {headers: this.headers}); return this.http.post<any>(`${this.url}oauth`, JSON.stringify(wizard), {headers: this.headers});
} }
} }

@ -0,0 +1,28 @@
import { PlatformLocation } from "@angular/common";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Rx";
import { IPlexPin } from "../../interfaces";
@Injectable()
export class PlexTvService {
constructor(private http: HttpClient, public platformLocation: PlatformLocation) {
}
public GetPin(clientId: string, applicationName: string): Observable<IPlexPin> {
const headers = new HttpHeaders({"Content-Type":"application/json",
"X-Plex-Client-Identifier": clientId,
"X-Plex-Product": applicationName,
"X-Plex-Version": "3",
"X-Plex-Device": "Ombi (Web)",
"X-Plex-Platform": "Web",
"Accept": "application/json",
});
return this.http.post<IPlexPin>("https://plex.tv/api/v2/pins?strong=true", null, {headers});
}
}

@ -95,6 +95,10 @@ export class SettingsService extends ServiceHelpers {
return this.http.get<IAuthenticationSettings>(`${this.url}/Authentication`, {headers: this.headers}); return this.http.get<IAuthenticationSettings>(`${this.url}/Authentication`, {headers: this.headers});
} }
public getClientId(): Observable<string> {
return this.http.get<string>(`${this.url}/clientid`, {headers: this.headers});
}
public saveAuthentication(settings: IAuthenticationSettings): Observable<boolean> { public saveAuthentication(settings: IAuthenticationSettings): Observable<boolean> {
return this.http.post<boolean>(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers}); return this.http.post<boolean>(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers});
} }

@ -1,20 +1,27 @@
import { Component } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { PlexService } from "../../services"; import { PlexService, PlexTvService, SettingsService } from "../../services";
import { IdentityService, NotificationService } from "../../services"; import { IdentityService, NotificationService } from "../../services";
@Component({ @Component({
templateUrl: "./plex.component.html", templateUrl: "./plex.component.html",
}) })
export class PlexComponent { export class PlexComponent implements OnInit {
public login: string; public login: string;
public password: string; public password: string;
private clientId: string;
constructor(private plexService: PlexService, private router: Router, constructor(private plexService: PlexService, private router: Router,
private notificationService: NotificationService, private notificationService: NotificationService,
private identityService: IdentityService) { } private identityService: IdentityService, private plexTv: PlexTvService,
private settingsService: SettingsService) { }
public ngOnInit(): void {
this.settingsService.getClientId().subscribe(x => this.clientId = x);
}
public requestAuthToken() { public requestAuthToken() {
this.plexService.logIn(this.login, this.password).subscribe(x => { this.plexService.logIn(this.login, this.password).subscribe(x => {
@ -40,10 +47,12 @@ export class PlexComponent {
} }
public oauth() { public oauth() {
this.plexService.oAuth(true).subscribe(x => { this.plexTv.GetPin(this.clientId, "Ombi").subscribe(pin => {
if(x.url) { this.plexService.oAuth({wizard: true, pin}).subscribe(x => {
window.location.href = x.url; if(x.url) {
} window.location.href = x.url;
}
});
}); });
} }
} }

@ -12,6 +12,7 @@ using Ombi.Core.Authentication;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; using Ombi.Core.Settings.Models.External;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Models;
using Ombi.Models.External; using Ombi.Models.External;
namespace Ombi.Controllers.External namespace Ombi.Controllers.External
@ -177,25 +178,23 @@ namespace Ombi.Controllers.External
return vm.DistinctBy(x => x.Id); return vm.DistinctBy(x => x.Id);
} }
[HttpGet("oauth/{wizard:bool}")] [HttpPost("oauth")]
[AllowAnonymous] [AllowAnonymous]
public async Task<IActionResult> OAuth(bool wizard) public async Task<IActionResult> OAuth([FromBody]PlexOAuthViewModel wizard)
{ {
//https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd
// Plex OAuth // Plex OAuth
// Redirect them to Plex // Redirect them to Plex
// We need a PIN first
var pin = await _plexOAuthManager.RequestPin();
Uri url; Uri url;
if (!wizard) if (!wizard.Wizard)
{ {
url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code); url = await _plexOAuthManager.GetOAuthUrl(wizard.Pin.id, wizard.Pin.code);
} }
else else
{ {
var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
url = _plexOAuthManager.GetWizardOAuthUrl(pin.id, pin.code, websiteAddress); url = await _plexOAuthManager.GetWizardOAuthUrl(wizard.Pin.id, wizard.Pin.code, websiteAddress);
} }
if (url == null) if (url == null)

@ -127,10 +127,23 @@ namespace Ombi.Controllers
public async Task<PlexSettings> PlexSettings() public async Task<PlexSettings> PlexSettings()
{ {
var s = await Get<PlexSettings>(); var s = await Get<PlexSettings>();
return s; return s;
} }
[HttpGet("clientid")]
[AllowAnonymous]
public async Task<string> GetClientId()
{
var s = await Get<PlexSettings>();
if (s.InstallId == Guid.Empty)
{
s.InstallId = Guid.NewGuid();
// Save it
await PlexSettings(s);
}
return s.InstallId.ToString("N");
}
/// <summary> /// <summary>
/// Save the Plex settings. /// Save the Plex settings.
/// </summary> /// </summary>
@ -139,6 +152,10 @@ namespace Ombi.Controllers
[HttpPost("plex")] [HttpPost("plex")]
public async Task<bool> PlexSettings([FromBody]PlexSettings plex) public async Task<bool> PlexSettings([FromBody]PlexSettings plex)
{ {
if (plex.InstallId == null || plex.InstallId == Guid.Empty)
{
plex.InstallId = Guid.NewGuid();
}
var result = await Save(plex); var result = await Save(plex);
return result; return result;
} }

@ -80,12 +80,10 @@ namespace Ombi.Controllers
{ {
// Plex OAuth // Plex OAuth
// Redirect them to Plex // Redirect them to Plex
// We need a PIN first
var pin = await _plexOAuthManager.RequestPin();
var websiteAddress = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; var websiteAddress = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
//https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd
var url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code, websiteAddress); var url = await _plexOAuthManager.GetOAuthUrl(model.PlexTvPin.id, model.PlexTvPin.code, websiteAddress);
if (url == null) if (url == null)
{ {
return new JsonResult(new return new JsonResult(new

@ -0,0 +1,10 @@
using Ombi.Api.Plex.Models.OAuth;
namespace Ombi.Models
{
public class PlexOAuthViewModel
{
public bool Wizard { get; set; }
public OAuthPin Pin { get; set; }
}
}

@ -1,4 +1,6 @@
namespace Ombi.Models using Ombi.Api.Plex.Models.OAuth;
namespace Ombi.Models
{ {
public class UserAuthModel public class UserAuthModel
{ {
@ -7,5 +9,6 @@
public bool RememberMe { get; set; } public bool RememberMe { get; set; }
public bool UsePlexAdminAccount { get; set; } public bool UsePlexAdminAccount { get; set; }
public bool UsePlexOAuth { get; set; } public bool UsePlexOAuth { get; set; }
public OAuthPin PlexTvPin { get; set; }
} }
} }

@ -129,6 +129,12 @@ namespace Ombi
//x.UseConsole(); //x.UseConsole();
}); });
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
// Build the intermediate service provider // Build the intermediate service provider
return services.BuildServiceProvider(); return services.BuildServiceProvider();
@ -211,13 +217,16 @@ namespace Ombi
app.UseMiddleware<ErrorHandlingMiddleware>(); app.UseMiddleware<ErrorHandlingMiddleware>();
app.UseMiddleware<ApiKeyMiddlewear>(); app.UseMiddleware<ApiKeyMiddlewear>();
app.UseCors("MyPolicy");
//app.ApiKeyMiddlewear(app.ApplicationServices); //app.ApiKeyMiddlewear(app.ApplicationServices);
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(c => app.UseSwaggerUI(c =>
{ {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
}); });
app.UseMvc(routes => app.UseMvc(routes =>
{ {
routes.MapRoute( routes.MapRoute(

Loading…
Cancel
Save