mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 10:36:54 -07:00
Merge pull request #2174 from tidusjar/feature/plexoauth
Added Plex OAuth into Ombi
This commit is contained in:
commit
4e8daeecd7
31 changed files with 691 additions and 84 deletions
|
@ -1,6 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.Friends;
|
||||
using Ombi.Api.Plex.Models.OAuth;
|
||||
using Ombi.Api.Plex.Models.Server;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
|
||||
|
@ -20,5 +22,8 @@ namespace Ombi.Api.Plex
|
|||
Task<PlexFriends> GetUsers(string authToken);
|
||||
Task<PlexAccount> GetAccount(string authToken);
|
||||
Task<PlexMetadata> GetRecentlyAdded(string authToken, string uri, string sectionId);
|
||||
Task<OAuthPin> CreatePin();
|
||||
Task<OAuthPin> GetPin(int pinId);
|
||||
Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard);
|
||||
}
|
||||
}
|
27
src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs
Normal file
27
src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Api.Plex.Models.OAuth
|
||||
{
|
||||
public class OAuthPin
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string code { get; set; }
|
||||
public bool trusted { get; set; }
|
||||
public string clientIdentifier { get; set; }
|
||||
public Location location { get; set; }
|
||||
public int expiresIn { get; set; }
|
||||
public DateTime createdAt { get; set; }
|
||||
public DateTime expiresAt { get; set; }
|
||||
public string authToken { get; set; }
|
||||
}
|
||||
|
||||
public class Location
|
||||
{
|
||||
public string code { get; set; }
|
||||
public string country { get; set; }
|
||||
public string city { get; set; }
|
||||
public string subdivisions { get; set; }
|
||||
public string coordinates { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +1,53 @@
|
|||
using System.Net.Http;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.Friends;
|
||||
using Ombi.Api.Plex.Models.OAuth;
|
||||
using Ombi.Api.Plex.Models.Server;
|
||||
using Ombi.Api.Plex.Models.Status;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
||||
namespace Ombi.Api.Plex
|
||||
{
|
||||
public class PlexApi : IPlexApi
|
||||
{
|
||||
public PlexApi(IApi api)
|
||||
public PlexApi(IApi api, ISettingsService<CustomizationSettings> settings)
|
||||
{
|
||||
Api = api;
|
||||
_custom = settings;
|
||||
}
|
||||
|
||||
private IApi Api { get; }
|
||||
private readonly ISettingsService<CustomizationSettings> _custom;
|
||||
|
||||
private string _app;
|
||||
private string ApplicationName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_app))
|
||||
{
|
||||
var settings = _custom.GetSettings();
|
||||
if (settings.ApplicationName.IsNullOrEmpty())
|
||||
{
|
||||
_app = "Ombi";
|
||||
}
|
||||
else
|
||||
{
|
||||
_app = settings.ApplicationName;
|
||||
}
|
||||
|
||||
return _app;
|
||||
}
|
||||
|
||||
return _app;
|
||||
}
|
||||
}
|
||||
|
||||
private const string SignInUri = "https://plex.tv/users/sign_in.json";
|
||||
private const string FriendsUri = "https://plex.tv/pms/friends/all";
|
||||
|
@ -156,6 +189,50 @@ namespace Ombi.Api.Plex
|
|||
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)
|
||||
{
|
||||
var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get);
|
||||
AddHeaders(request);
|
||||
|
||||
return await Api.Request<OAuthPin>(request);
|
||||
}
|
||||
|
||||
public Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard)
|
||||
{
|
||||
var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get);
|
||||
AddHeaders(request);
|
||||
var forwardUrl = wizard
|
||||
? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get)
|
||||
: new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get);
|
||||
|
||||
request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString());
|
||||
request.AddQueryString("pinID", pinId.ToString());
|
||||
request.AddQueryString("code", code);
|
||||
request.AddQueryString("context[device][product]", "Ombi");
|
||||
request.AddQueryString("context[device][environment]", "bundled");
|
||||
request.AddQueryString("clientID", $"OmbiV3");
|
||||
|
||||
if (request.FullUri.Fragment.Equals("#"))
|
||||
{
|
||||
var uri = request.FullUri.ToString();
|
||||
var withoutEnd = uri.Remove(uri.Length - 1, 1);
|
||||
var startOfQueryLocation = withoutEnd.IndexOf('?');
|
||||
var better = withoutEnd.Insert(startOfQueryLocation, "#");
|
||||
request.FullUri = new Uri(better);
|
||||
}
|
||||
|
||||
return request.FullUri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the required headers and also the authorization header
|
||||
/// </summary>
|
||||
|
@ -174,7 +251,7 @@ namespace Ombi.Api.Plex
|
|||
private void AddHeaders(Request request)
|
||||
{
|
||||
request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3");
|
||||
request.AddHeader("X-Plex-Product", "Ombi");
|
||||
request.AddHeader("X-Plex-Product", ApplicationName);
|
||||
request.AddHeader("X-Plex-Version", "3");
|
||||
request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml");
|
||||
request.AddHeader("Accept", "application/json");
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Ombi.Api
|
|||
{
|
||||
public Request()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public Request(string endpoint, string baseUrl, HttpMethod http, ContentType contentType = ContentType.Json)
|
||||
|
@ -105,10 +105,10 @@ namespace Ombi.Api
|
|||
hasQuery = true;
|
||||
startingTag = builder.Query.Contains("?") ? "&" : "?";
|
||||
}
|
||||
|
||||
builder.Query = hasQuery
|
||||
? $"{builder.Query}{startingTag}{key}={value}"
|
||||
: $"{startingTag}{key}={value}";
|
||||
|
||||
_modified = builder.Uri;
|
||||
}
|
||||
|
||||
|
|
76
src/Ombi.Core/Authentication/PlexOAuthManager.cs
Normal file
76
src/Ombi.Core/Authentication/PlexOAuthManager.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.OAuth;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
|
||||
namespace Ombi.Core.Authentication
|
||||
{
|
||||
public class PlexOAuthManager : IPlexOAuthManager
|
||||
{
|
||||
public PlexOAuthManager(IPlexApi api, ISettingsService<CustomizationSettings> settings)
|
||||
{
|
||||
_api = api;
|
||||
_customizationSettingsService = settings;
|
||||
}
|
||||
|
||||
private readonly IPlexApi _api;
|
||||
private readonly ISettingsService<CustomizationSettings> _customizationSettingsService;
|
||||
|
||||
public async Task<OAuthPin> RequestPin()
|
||||
{
|
||||
var pin = await _api.CreatePin();
|
||||
return pin;
|
||||
}
|
||||
|
||||
public async Task<string> GetAccessTokenFromPin(int pinId)
|
||||
{
|
||||
var pin = await _api.GetPin(pinId);
|
||||
if (pin.expiresAt < DateTime.UtcNow)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (pin.authToken.IsNullOrEmpty())
|
||||
{
|
||||
// Looks like we do not have a pin yet, we should retry a few times.
|
||||
var retryCount = 0;
|
||||
var retryMax = 5;
|
||||
var retryWaitMs = 1000;
|
||||
while (pin.authToken.IsNullOrEmpty() && retryCount < retryMax)
|
||||
{
|
||||
retryCount++;
|
||||
await Task.Delay(retryWaitMs);
|
||||
pin = await _api.GetPin(pinId);
|
||||
}
|
||||
}
|
||||
return pin.authToken;
|
||||
}
|
||||
|
||||
public async Task<PlexAccount> GetAccount(string accessToken)
|
||||
{
|
||||
return await _api.GetAccount(accessToken);
|
||||
}
|
||||
|
||||
public async Task<Uri> GetOAuthUrl(int pinId, string code)
|
||||
{
|
||||
var settings = await _customizationSettingsService.GetSettingsAsync();
|
||||
if (settings.ApplicationUrl.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl, false);
|
||||
return url;
|
||||
}
|
||||
|
||||
public Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress)
|
||||
{
|
||||
var url = _api.GetOAuthUrl(pinId, code, websiteAddress, true);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
16
src/Ombi.Core/IPlexOAuthManager.cs
Normal file
16
src/Ombi.Core/IPlexOAuthManager.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Api.Plex.Models.OAuth;
|
||||
|
||||
namespace Ombi.Core.Authentication
|
||||
{
|
||||
public interface IPlexOAuthManager
|
||||
{
|
||||
Task<string> GetAccessTokenFromPin(int pinId);
|
||||
Task<OAuthPin> RequestPin();
|
||||
Task<Uri> GetOAuthUrl(int pinId, string code);
|
||||
Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress);
|
||||
Task<PlexAccount> GetAccount(string accessToken);
|
||||
}
|
||||
}
|
|
@ -51,6 +51,7 @@ using Ombi.Store.Repository.Requests;
|
|||
using Ombi.Updater;
|
||||
using PlexContentCacher = Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Api.Telegram;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Processor;
|
||||
using Ombi.Schedule.Jobs.Plex.Interfaces;
|
||||
using Ombi.Schedule.Jobs.SickRage;
|
||||
|
@ -82,6 +83,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IRecentlyAddedEngine, RecentlyAddedEngine>();
|
||||
services.AddTransient<ITvSender, TvSender>();
|
||||
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
||||
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
|
||||
}
|
||||
public static void RegisterHttp(this IServiceCollection services)
|
||||
{
|
||||
|
|
|
@ -7,7 +7,6 @@ namespace Ombi.Core.Settings.Models.External
|
|||
{
|
||||
public bool Enable { get; set; }
|
||||
public List<PlexServers> Servers { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class PlexServers : ExternalSettings
|
||||
|
|
|
@ -24,6 +24,7 @@ import { CookieComponent } from "./auth/cookie.component";
|
|||
import { PageNotFoundComponent } from "./errors/not-found.component";
|
||||
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
||||
import { LoginComponent } from "./login/login.component";
|
||||
import { LoginOAuthComponent } from "./login/loginoauth.component";
|
||||
import { ResetPasswordComponent } from "./login/resetpassword.component";
|
||||
import { TokenResetPasswordComponent } from "./login/tokenresetpassword.component";
|
||||
|
||||
|
@ -41,6 +42,7 @@ const routes: Routes = [
|
|||
{ path: "*", component: PageNotFoundComponent },
|
||||
{ path: "", redirectTo: "/search", pathMatch: "full" },
|
||||
{ path: "login", component: LoginComponent },
|
||||
{ path: "Login/OAuth/:pin", component: LoginOAuthComponent },
|
||||
{ path: "login/:landing", component: LoginComponent },
|
||||
{ path: "reset", component: ResetPasswordComponent },
|
||||
{ path: "token", component: TokenResetPasswordComponent },
|
||||
|
@ -116,6 +118,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo
|
|||
ResetPasswordComponent,
|
||||
TokenResetPasswordComponent,
|
||||
CookieComponent,
|
||||
LoginOAuthComponent,
|
||||
],
|
||||
providers: [
|
||||
NotificationService,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
username: string;
|
||||
password: string;
|
||||
rememberMe: boolean;
|
||||
usePlexOAuth: boolean;
|
||||
}
|
||||
|
||||
export interface ILocalUser {
|
||||
|
|
|
@ -18,6 +18,10 @@ export class AuthService extends ServiceHelpers {
|
|||
return this.http.post(`${this.url}/`, JSON.stringify(login), {headers: this.headers});
|
||||
}
|
||||
|
||||
public oAuth(pin: number): Observable<any> {
|
||||
return this.http.get<any>(`${this.url}/${pin}`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public requiresPassword(login: IUserLogin): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}/requirePassword`, JSON.stringify(login), {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
user: IPlexUser;
|
||||
}
|
||||
|
||||
export interface IPlexOAuthAccessToken {
|
||||
accessToken: string;
|
||||
}
|
||||
|
||||
export interface IPlexUser {
|
||||
email: string;
|
||||
uuid: string;
|
||||
|
|
|
@ -7,11 +7,13 @@ include the remember me checkbox
|
|||
<div *ngIf="background" @fadeInOut class="bg" [style.background-image]="background"></div>
|
||||
<div class="container" id="login">
|
||||
<div class="card card-container">
|
||||
|
||||
<!-- <img class="profile-img-card" src="//lh3.googleusercontent.com/-6V8xOA6M7BA/AAAAAAAAAAI/AAAAAAAAAAA/rzlHcD0KYwo/photo.jpg?sz=120" alt="" /> -->
|
||||
<div *ngIf="!customizationSettings.logo"><img id="profile-img" class="profile-img-card" src="{{baseUrl}}/images/logo.png"/></div>
|
||||
<div *ngIf="customizationSettings.logo"><img id="profile-img" class="center" [src]="customizationSettings.logo" /></div>
|
||||
<p id="profile-name" class="profile-name-card"></p>
|
||||
|
||||
<div *ngIf="showLoginForm && loginWithOmbi">
|
||||
<form *ngIf="authenticationSettings" class="form-signin" novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
|
||||
|
||||
|
||||
|
@ -27,6 +29,16 @@ include the remember me checkbox
|
|||
</div>
|
||||
<button class="btn btn-success" type="submit" [translate]="'Login.SignInButton'"></button>
|
||||
</form><!-- /form -->
|
||||
</div>
|
||||
<div class="form-signin" *ngIf="plexEnabled && customizationSettings.applicationUrl && !loginWithOmbi">
|
||||
<button class="btn btn-success" type="button" (click)="loginWithOmbi = true">
|
||||
Continue With {{appName}}</button>
|
||||
</div>
|
||||
<div class="form-signin" *ngIf="plexEnabled && customizationSettings.applicationUrl && !loginWithOmbi">
|
||||
<button class="btn btn-primary" type="button" (click)="oauth()">
|
||||
Continue With Plex</button>
|
||||
</div>
|
||||
|
||||
<a [routerLink]="['/reset']" class="forgot-password col-md-12">
|
||||
<b [translate]="'Login.ForgottenPassword'"></b>
|
||||
</a>
|
||||
|
|
|
@ -25,9 +25,38 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
public form: FormGroup;
|
||||
public customizationSettings: ICustomizationSettings;
|
||||
public authenticationSettings: IAuthenticationSettings;
|
||||
public plexEnabled: boolean;
|
||||
public background: any;
|
||||
public landingFlag: boolean;
|
||||
public baseUrl: string;
|
||||
|
||||
public get showLoginForm(): boolean {
|
||||
if(this.customizationSettings.applicationUrl && this.plexEnabled) {
|
||||
this.loginWithOmbi = false;
|
||||
return false;
|
||||
}
|
||||
if(!this.customizationSettings.applicationUrl || !this.plexEnabled) {
|
||||
|
||||
this.loginWithOmbi = true;
|
||||
return true;
|
||||
}
|
||||
if(this.loginWithOmbi) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.loginWithOmbi = true;
|
||||
return true;
|
||||
}
|
||||
public loginWithOmbi: boolean = false;
|
||||
|
||||
public get appName(): string {
|
||||
if(this.customizationSettings.applicationName) {
|
||||
return this.customizationSettings.applicationName;
|
||||
} else {
|
||||
return "Ombi";
|
||||
}
|
||||
}
|
||||
|
||||
private timer: any;
|
||||
|
||||
private errorBody: string;
|
||||
|
@ -68,6 +97,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
public ngOnInit() {
|
||||
this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x);
|
||||
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
|
||||
this.settingsService.getStatusPlex().subscribe(x => this.plexEnabled = 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 + ")");
|
||||
});
|
||||
|
@ -90,7 +120,7 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
return;
|
||||
}
|
||||
const value = form.value;
|
||||
const user = { password: value.password, username: value.username, rememberMe:value.rememberMe };
|
||||
const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false };
|
||||
this.authService.requiresPassword(user).subscribe(x => {
|
||||
if(x && this.authenticationSettings.allowNoPassword) {
|
||||
// Looks like this user requires a password
|
||||
|
@ -111,6 +141,12 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
public oauth() {
|
||||
this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => {
|
||||
window.location.href = x.url;
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
@ -124,5 +160,4 @@ export class LoginComponent implements OnDestroy, OnInit {
|
|||
.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 + ")");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
9
src/Ombi/ClientApp/app/login/loginoauth.component.html
Normal file
9
src/Ombi/ClientApp/app/login/loginoauth.component.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
<div >
|
||||
|
||||
<div class="container" id="login">
|
||||
<div class="card card-container">
|
||||
<label>Please Wait...</label>
|
||||
</div><!-- /card-container -->
|
||||
</div><!-- /container -->
|
||||
</div>
|
35
src/Ombi/ClientApp/app/login/loginoauth.component.ts
Normal file
35
src/Ombi/ClientApp/app/login/loginoauth.component.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./loginoauth.component.html",
|
||||
})
|
||||
export class LoginOAuthComponent implements OnInit {
|
||||
public pin: number;
|
||||
|
||||
constructor(private authService: AuthService, private router: Router,
|
||||
private route: ActivatedRoute) {
|
||||
this.route.params
|
||||
.subscribe((params: any) => {
|
||||
this.pin = params.pin;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.auth();
|
||||
}
|
||||
|
||||
public auth() {
|
||||
this.authService.oAuth(this.pin).subscribe(x => {
|
||||
localStorage.setItem("id_token", x.access_token);
|
||||
|
||||
if (this.authService.loggedIn()) {
|
||||
this.router.navigate(["search"]);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@ export * from "./plex.service";
|
|||
export * from "./radarr.service";
|
||||
export * from "./sonarr.service";
|
||||
export * from "./tester.service";
|
||||
export * from "./plexoauth.service";
|
||||
|
|
|
@ -29,4 +29,8 @@ export class PlexService extends ServiceHelpers {
|
|||
public getFriends(): Observable<IUsersModel[]> {
|
||||
return this.http.get<IUsersModel[]>(`${this.url}Friends`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public oAuth(wizard: boolean): Observable<any> {
|
||||
return this.http.get<any>(`${this.url}oauth/${wizard}`, {headers: this.headers});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { PlatformLocation } from "@angular/common";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Observable } from "rxjs/Rx";
|
||||
|
||||
import { ServiceHelpers } from "../service.helpers";
|
||||
|
||||
import { IPlexOAuthAccessToken } from "../../interfaces";
|
||||
|
||||
@Injectable()
|
||||
export class PlexOAuthService extends ServiceHelpers {
|
||||
constructor(http: HttpClient, public platformLocation: PlatformLocation) {
|
||||
super(http, "/api/v1/PlexOAuth/", platformLocation);
|
||||
}
|
||||
|
||||
public oAuth(pin: number): Observable<IPlexOAuthAccessToken> {
|
||||
return this.http.get<IPlexOAuthAccessToken>(`${this.url}${pin}`, {headers: this.headers});
|
||||
}
|
||||
}
|
|
@ -71,6 +71,10 @@ export class SettingsService extends ServiceHelpers {
|
|||
return this.http.get<IPlexSettings>(`${this.url}/Plex/`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public getStatusPlex(): Observable<boolean> {
|
||||
return this.http.get<boolean>(`${this.url}/Plexstatus/`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public savePlex(settings: IPlexSettings): Observable<boolean> {
|
||||
return this.http.post<boolean>(`${this.url}/Plex/`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class CreateAdminComponent {
|
|||
this.identityService.createWizardUser({username: this.username, password: this.password, usePlexAdminAccount: false}).subscribe(x => {
|
||||
if (x) {
|
||||
// Log me in.
|
||||
this.auth.login({ username: this.username, password: this.password, rememberMe:false }).subscribe(c => {
|
||||
this.auth.login({ username: this.username, password: this.password, rememberMe: false, usePlexOAuth:false }).subscribe(c => {
|
||||
|
||||
localStorage.setItem("id_token", c.access_token);
|
||||
|
||||
|
|
|
@ -17,9 +17,16 @@
|
|||
<small>Please note we do not store this information, we only store your Plex Authorization Token that will allow Ombi to view your media and friends</small>
|
||||
<div class="form-group">
|
||||
<div style="text-align: center; margin-top: 20px">
|
||||
<button (click)="requestAuthToken()" class="btn btn-primary-outline">Request Token <i class="fa fa-key"></i></button>
|
||||
<button (click)="requestAuthToken()" class="btn btn-success-outline">Request Token <i class="fa fa-key"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-center">OR</p>
|
||||
<div class="form-group">
|
||||
<div style="text-align: center; margin-top: 20px">
|
||||
<button (click)="oauth()" class="btn btn-primary" type="button">Continue With Plex</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ConfirmationService } from "primeng/primeng";
|
||||
|
||||
import { PlexService } from "../../services";
|
||||
import { IdentityService, NotificationService, SettingsService } from "../../services";
|
||||
import { AuthService } from "./../../auth/auth.service";
|
||||
|
@ -17,7 +15,6 @@ export class PlexComponent {
|
|||
|
||||
constructor(private plexService: PlexService, private router: Router,
|
||||
private notificationService: NotificationService,
|
||||
private confirmationService: ConfirmationService,
|
||||
private identityService: IdentityService,
|
||||
private settings: SettingsService,
|
||||
private auth: AuthService) { }
|
||||
|
@ -28,25 +25,21 @@ export class PlexComponent {
|
|||
this.notificationService.error("Username or password was incorrect. Could not authenticate with Plex.");
|
||||
return;
|
||||
}
|
||||
this.confirmationService.confirm({
|
||||
message: "Do you want your Plex user to be the main admin account on Ombi?",
|
||||
header: "Use Plex Account",
|
||||
icon: "fa fa-check",
|
||||
accept: () => {
|
||||
this.identityService.createWizardUser({
|
||||
|
||||
this.identityService.createWizardUser({
|
||||
username: "",
|
||||
password: "",
|
||||
usePlexAdminAccount: true,
|
||||
}).subscribe(x => {
|
||||
if (x) {
|
||||
this.auth.login({ username: this.login, password: this.password, rememberMe:false }).subscribe(c => {
|
||||
}).subscribe(y => {
|
||||
if (y) {
|
||||
this.auth.login({ username: this.login, password: this.password, rememberMe: false, usePlexOAuth: false }).subscribe(c => {
|
||||
localStorage.setItem("id_token", c.access_token);
|
||||
|
||||
// Mark that we have done the settings now
|
||||
this.settings.getOmbi().subscribe(ombi => {
|
||||
ombi.wizard = true;
|
||||
|
||||
this.settings.saveOmbi(ombi).subscribe(x => {
|
||||
this.settings.saveOmbi(ombi).subscribe(s => {
|
||||
this.settings.getUserManagementSettings().subscribe(usr => {
|
||||
|
||||
usr.importPlexAdmin = true;
|
||||
|
@ -64,10 +57,14 @@ export class PlexComponent {
|
|||
}
|
||||
});
|
||||
},
|
||||
reject: () => {
|
||||
this.router.navigate(["Wizard/CreateAdmin"]);
|
||||
},
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
public oauth() {
|
||||
this.plexService.oAuth(true).subscribe(x => {
|
||||
if(x.url) {
|
||||
window.location.href = x.url;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
14
src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html
Normal file
14
src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
<img class="landing-header" src="/images/logo.png" width="300" />
|
||||
<div class="landing-block shadow">
|
||||
<div class="media">
|
||||
<div id="contentBody" class="media-body">
|
||||
<h4 class="media-heading landing-title">Plex Authentication</h4>
|
||||
<div class="form-group">
|
||||
<label for="username" class="control-label">Please Wait</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p-confirmDialog></p-confirmDialog>
|
67
src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts
Normal file
67
src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { IdentityService, PlexOAuthService, SettingsService } from "../../services";
|
||||
import { AuthService } from "./../../auth/auth.service";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./plexoauth.component.html",
|
||||
})
|
||||
export class PlexOAuthComponent implements OnInit {
|
||||
public pinId: number;
|
||||
|
||||
constructor(private route: ActivatedRoute,
|
||||
private plexOauth: PlexOAuthService,
|
||||
private identityService: IdentityService,
|
||||
private settings: SettingsService,
|
||||
private router: Router,
|
||||
private auth: AuthService) {
|
||||
|
||||
this.route.params
|
||||
.subscribe((params: any) => {
|
||||
this.pinId = params.pin;
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.plexOauth.oAuth(this.pinId).subscribe(x => {
|
||||
if(!x.accessToken) {
|
||||
return;
|
||||
// RETURN
|
||||
}
|
||||
|
||||
this.identityService.createWizardUser({
|
||||
username: "",
|
||||
password: "",
|
||||
usePlexAdminAccount: true,
|
||||
}).subscribe(u => {
|
||||
if (u) {
|
||||
this.auth.oAuth(this.pinId).subscribe(c => {
|
||||
localStorage.setItem("id_token", c.access_token);
|
||||
|
||||
// Mark that we have done the settings now
|
||||
this.settings.getOmbi().subscribe(ombi => {
|
||||
ombi.wizard = true;
|
||||
|
||||
this.settings.saveOmbi(ombi).subscribe(s => {
|
||||
this.settings.getUserManagementSettings().subscribe(usr => {
|
||||
|
||||
usr.importPlexAdmin = true;
|
||||
this.settings.saveUserManagementSettings(usr).subscribe(saved => {
|
||||
this.router.navigate(["login"]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//this.notificationService.error("Could not get the Plex Admin Information");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,8 @@ import { WelcomeComponent } from "./welcome/welcome.component";
|
|||
import { EmbyService } from "../services";
|
||||
import { PlexService } from "../services";
|
||||
import { IdentityService } from "../services";
|
||||
import { PlexOAuthService } from "../services";
|
||||
import { PlexOAuthComponent } from "./plex/plexoauth.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: "", component: WelcomeComponent},
|
||||
|
@ -21,6 +23,7 @@ const routes: Routes = [
|
|||
{ path: "Plex", component: PlexComponent},
|
||||
{ path: "Emby", component: EmbyComponent},
|
||||
{ path: "CreateAdmin", component: CreateAdminComponent},
|
||||
{ path: "OAuth/:pin", component: PlexOAuthComponent},
|
||||
];
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -33,6 +36,7 @@ const routes: Routes = [
|
|||
WelcomeComponent,
|
||||
MediaServerComponent,
|
||||
PlexComponent,
|
||||
PlexOAuthComponent,
|
||||
CreateAdminComponent,
|
||||
EmbyComponent,
|
||||
],
|
||||
|
@ -44,6 +48,7 @@ const routes: Routes = [
|
|||
IdentityService,
|
||||
EmbyService,
|
||||
ConfirmationService,
|
||||
PlexOAuthService,
|
||||
],
|
||||
|
||||
})
|
||||
|
|
37
src/Ombi/Controllers/External/PlexController.cs
vendored
37
src/Ombi/Controllers/External/PlexController.cs
vendored
|
@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
|
|||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
|
@ -21,16 +22,18 @@ namespace Ombi.Controllers.External
|
|||
public class PlexController : Controller
|
||||
{
|
||||
public PlexController(IPlexApi plexApi, ISettingsService<PlexSettings> plexSettings,
|
||||
ILogger<PlexController> logger)
|
||||
ILogger<PlexController> logger, IPlexOAuthManager manager)
|
||||
{
|
||||
PlexApi = plexApi;
|
||||
PlexSettings = plexSettings;
|
||||
_log = logger;
|
||||
_plexOAuthManager = manager;
|
||||
}
|
||||
|
||||
private IPlexApi PlexApi { get; }
|
||||
private ISettingsService<PlexSettings> PlexSettings { get; }
|
||||
private readonly ILogger<PlexController> _log;
|
||||
private readonly IPlexOAuthManager _plexOAuthManager;
|
||||
|
||||
/// <summary>
|
||||
/// Signs into the Plex API.
|
||||
|
@ -173,5 +176,37 @@ namespace Ombi.Controllers.External
|
|||
// Filter out any dupes
|
||||
return vm.DistinctBy(x => x.Id);
|
||||
}
|
||||
|
||||
[HttpGet("oauth/{wizard:bool}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> OAuth(bool wizard)
|
||||
{
|
||||
//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
|
||||
// Redirect them to Plex
|
||||
// We need a PIN first
|
||||
var pin = await _plexOAuthManager.RequestPin();
|
||||
|
||||
Uri url;
|
||||
if (!wizard)
|
||||
{
|
||||
url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code);
|
||||
}
|
||||
else
|
||||
{
|
||||
var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
|
||||
url = _plexOAuthManager.GetWizardOAuthUrl(pin.id, pin.code, websiteAddress);
|
||||
}
|
||||
|
||||
if (url == null)
|
||||
{
|
||||
return new JsonResult(new
|
||||
{
|
||||
error = "Application URL has not been set"
|
||||
});
|
||||
}
|
||||
|
||||
return new JsonResult(new {url = url.ToString()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
78
src/Ombi/Controllers/PlexOAuthController.cs
Normal file
78
src/Ombi/Controllers/PlexOAuthController.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Controllers
|
||||
{
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[ApiV1]
|
||||
[AllowAnonymous]
|
||||
public class PlexOAuthController : Controller
|
||||
{
|
||||
public PlexOAuthController(IPlexOAuthManager manager, IPlexApi plexApi, ISettingsService<PlexSettings> plexSettings,
|
||||
ILogger<PlexOAuthController> log)
|
||||
{
|
||||
_manager = manager;
|
||||
_plexApi = plexApi;
|
||||
_plexSettings = plexSettings;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
private readonly IPlexOAuthManager _manager;
|
||||
private readonly IPlexApi _plexApi;
|
||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||
private readonly ILogger _log;
|
||||
|
||||
[HttpGet("{pinId:int}")]
|
||||
public async Task<IActionResult> OAuthWizardCallBack([FromRoute] int pinId)
|
||||
{
|
||||
var accessToken = await _manager.GetAccessTokenFromPin(pinId);
|
||||
if (accessToken.IsNullOrEmpty())
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
error = "Authentication did not work. Please try again"
|
||||
});
|
||||
}
|
||||
var settings = await _plexSettings.GetSettingsAsync();
|
||||
var server = await _plexApi.GetServer(accessToken);
|
||||
var servers = server.Server.FirstOrDefault();
|
||||
if (servers == null)
|
||||
{
|
||||
_log.LogWarning("Looks like we can't find any Plex Servers");
|
||||
}
|
||||
_log.LogDebug("Adding first server");
|
||||
|
||||
settings.Enable = true;
|
||||
settings.Servers = new List<PlexServers> {
|
||||
new PlexServers
|
||||
{
|
||||
PlexAuthToken = accessToken,
|
||||
Id = new Random().Next(),
|
||||
Ip = servers?.LocalAddresses?.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries)?.FirstOrDefault() ?? string.Empty,
|
||||
MachineIdentifier = servers?.MachineIdentifier ?? string.Empty,
|
||||
Port = int.Parse(servers?.Port ?? "0"),
|
||||
Ssl = (servers?.Scheme ?? "http") != "http",
|
||||
Name = "Server 1",
|
||||
}
|
||||
};
|
||||
|
||||
await _plexSettings.SaveSettingsAsync(settings);
|
||||
return Json(new { accessToken });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -142,7 +142,19 @@ namespace Ombi.Controllers
|
|||
[HttpGet("plex")]
|
||||
public async Task<PlexSettings> PlexSettings()
|
||||
{
|
||||
return await Get<PlexSettings>();
|
||||
var s = await Get<PlexSettings>();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
[HttpGet("plexstatus")]
|
||||
[AllowAnonymous]
|
||||
public async Task<bool> PlexStatusSettings()
|
||||
{
|
||||
var s = await Get<PlexSettings>();
|
||||
|
||||
|
||||
return s.Enable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Identity;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Ombi.Api;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Models;
|
||||
|
@ -23,18 +25,21 @@ namespace Ombi.Controllers
|
|||
[Produces("application/json")]
|
||||
public class TokenController
|
||||
{
|
||||
public TokenController(OmbiUserManager um, IOptions<TokenAuthentication> ta, IAuditRepository audit, ITokenRepository token)
|
||||
public TokenController(OmbiUserManager um, IOptions<TokenAuthentication> ta, IAuditRepository audit, ITokenRepository token,
|
||||
IPlexOAuthManager oAuthManager)
|
||||
{
|
||||
_userManager = um;
|
||||
_tokenAuthenticationOptions = ta.Value;
|
||||
_audit = audit;
|
||||
_token = token;
|
||||
_plexOAuthManager = oAuthManager;
|
||||
}
|
||||
|
||||
private readonly TokenAuthentication _tokenAuthenticationOptions;
|
||||
private readonly IAuditRepository _audit;
|
||||
private readonly ITokenRepository _token;
|
||||
private readonly OmbiUserManager _userManager;
|
||||
private readonly IPlexOAuthManager _plexOAuthManager;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the token.
|
||||
|
@ -44,71 +49,123 @@ namespace Ombi.Controllers
|
|||
[HttpPost]
|
||||
public async Task<IActionResult> GetToken([FromBody] UserAuthModel model)
|
||||
{
|
||||
await _audit.Record(AuditType.None, AuditArea.Authentication,
|
||||
if (!model.UsePlexOAuth)
|
||||
{
|
||||
await _audit.Record(AuditType.None, AuditArea.Authentication,
|
||||
$"UserName {model.Username} attempting to authenticate");
|
||||
|
||||
var user = await _userManager.FindByNameAsync(model.Username);
|
||||
var user = await _userManager.FindByNameAsync(model.Username);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
// Could this be an email login?
|
||||
user = await _userManager.FindByEmailAsync(model.Username);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
|
||||
user.EmailLogin = true;
|
||||
}
|
||||
|
||||
|
||||
// Verify Password
|
||||
if (await _userManager.CheckPasswordAsync(user, model.Password))
|
||||
{
|
||||
return await CreateToken(model.RememberMe, user);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Plex OAuth
|
||||
// Redirect them to Plex
|
||||
// We need a PIN first
|
||||
var pin = await _plexOAuthManager.RequestPin();
|
||||
|
||||
//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);
|
||||
if (url == null)
|
||||
{
|
||||
return new JsonResult(new
|
||||
{
|
||||
error = "Application URL has not been set"
|
||||
});
|
||||
}
|
||||
return new JsonResult(new { url = url.ToString() });
|
||||
}
|
||||
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
|
||||
private async Task<IActionResult> CreateToken(bool rememberMe, OmbiUser user)
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
|
||||
if (roles.Contains(OmbiRoles.Disabled))
|
||||
{
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
|
||||
user.LastLoggedIn = DateTime.UtcNow;
|
||||
await _userManager.UpdateAsync(user);
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim(ClaimTypes.Name, user.UserName),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
claims.AddRange(roles.Select(role => new Claim("role", role)));
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
claims: claims,
|
||||
expires: rememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5),
|
||||
signingCredentials: creds,
|
||||
audience: "Ombi", issuer: "Ombi"
|
||||
);
|
||||
var accessToken = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
if (rememberMe)
|
||||
{
|
||||
// Save the token so we can refresh it later
|
||||
//await _token.CreateToken(new Tokens() {Token = accessToken, User = user});
|
||||
}
|
||||
|
||||
return new JsonResult(new
|
||||
{
|
||||
access_token = accessToken,
|
||||
expiration = token.ValidTo
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{pinId:int}")]
|
||||
public async Task<IActionResult> OAuth(int pinId)
|
||||
{
|
||||
var accessToken = await _plexOAuthManager.GetAccessTokenFromPin(pinId);
|
||||
|
||||
// Let's look for the users account
|
||||
var account = await _plexOAuthManager.GetAccount(accessToken);
|
||||
|
||||
// Get the ombi user
|
||||
var user = await _userManager.FindByNameAsync(account.user.username);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
// Could this be an email login?
|
||||
user = await _userManager.FindByEmailAsync(model.Username);
|
||||
user = await _userManager.FindByEmailAsync(account.user.email);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
|
||||
user.EmailLogin = true;
|
||||
}
|
||||
|
||||
// Verify Password
|
||||
if (await _userManager.CheckPasswordAsync(user, model.Password))
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
|
||||
if (roles.Contains(OmbiRoles.Disabled))
|
||||
{
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
|
||||
user.LastLoggedIn = DateTime.UtcNow;
|
||||
await _userManager.UpdateAsync(user);
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim(ClaimTypes.Name, user.UserName),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
claims.AddRange(roles.Select(role => new Claim("role", role)));
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
claims: claims,
|
||||
expires: model.RememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5),
|
||||
signingCredentials: creds,
|
||||
audience: "Ombi", issuer:"Ombi"
|
||||
);
|
||||
var accessToken = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
if (model.RememberMe)
|
||||
{
|
||||
// Save the token so we can refresh it later
|
||||
//await _token.CreateToken(new Tokens() {Token = accessToken, User = user});
|
||||
}
|
||||
|
||||
return new JsonResult(new
|
||||
{
|
||||
access_token = accessToken,
|
||||
expiration = token.ValidTo
|
||||
});
|
||||
}
|
||||
|
||||
return new UnauthorizedResult();
|
||||
return await CreateToken(true, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -127,7 +184,7 @@ namespace Ombi.Controllers
|
|||
{
|
||||
return new UnauthorizedResult();
|
||||
}
|
||||
|
||||
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
public string Password { get; set; }
|
||||
public bool RememberMe { get; set; }
|
||||
public bool UsePlexAdminAccount { get; set; }
|
||||
public bool UsePlexOAuth { get; set; }
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue