Skip to content

Commit

Permalink
Add authentication flow
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownskl committed Sep 25, 2024
1 parent cf4b6c1 commit 8956adf
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 99 deletions.
60 changes: 39 additions & 21 deletions app/main/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import Platform from '@greenlight/platform'
import Logger from '@greenlight/logger'
import pkg from '../package.json'
import MainWindow from './windows/main'
import AuthWindow from './windows/auth'
import ElectronStore from 'electron-store'

export default class Application {

public isProduction: boolean = process.env.NODE_ENV === 'production'
public logger:Logger = new Logger('GreenlightApp')
public store = new ElectronStore()

private _platform?:Platform
private _mainWindow?:MainWindow
Expand All @@ -22,19 +25,54 @@ export default class Application {
} else {
app.setPath('userData', `${app.getPath('userData')} (development)`)
}
app.setName('Greenlight')

// this.store.set('authentication.tokens', '{}')
}

async isReady() {
await app.whenReady()
}

getPlatform() {
return this._platform
}

loadPlatform(){
return new Promise((resolve, reject) => {
this._platform = new Platform()
this._platform.onAuthLoad = () => {
this.logger.log('onAuthSave() Loading tokens from disk...')
const tokens = this.store.get('authentication.tokens', '{}')

return {
action: 'auth_load',
tokens: JSON.stringify(tokens)
}
}

this._platform.onAuthSave = (tokens) => {
this.logger.log('onAuthSave() Saving tokens to disk...')
this.store.set('authentication.tokens', JSON.parse(tokens))
return true
}

this._platform.onAuthClear = () => {
this.logger.log('onAuthSave() Clearing tokens from disk...')
return true
}

this._platform.loadWorker(path.join(__dirname, 'worker.js')).then((authenticated:boolean) => {
this.logger.log('loadPlatform() Platform loaded and authenticated')
dialog.showErrorBox('Worker success', 'Platform worker loaded!')
// dialog.showErrorBox('Worker success', 'Platform worker loaded!')

this._platform?.api.Authentication.getGamertag()

if(authenticated === false){
// Not authenticated..
new AuthWindow(this)
}

resolve(authenticated)
}).catch((error:any) => {
this.logger.error('loadPlatform() Platform failed to load. Critical error: '+error)
Expand All @@ -45,26 +83,6 @@ export default class Application {
}

async spawnMainWindow() {
// const mainWindow = createWindow('main', {
// width: 1280,
// height: (this.isProduction) ? 800 : 1200,
// title: 'Greenlight',
// backgroundColor: 'rgb(26, 27, 30)',
// webPreferences: {
// preload: path.join(__dirname, 'preload.js'),
// },
// })

// if (this.isProduction) {
// await mainWindow.loadURL('app://./boot')
// } else {
// const port = process.argv[2]
// await mainWindow.loadURL(`http://localhost:${port}/boot`)
// mainWindow.webContents.openDevTools({
// mode: 'bottom'
// })
// }
// this.logger.log('spawnMainWindow() Main application windows drawn')

this._mainWindow = new MainWindow(this)
}
Expand Down
43 changes: 34 additions & 9 deletions app/main/windows/auth.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { session } from 'electron'
import Logger from '@greenlight/logger'
import path from 'path'
import Application from '../background'
import { createWindow } from '../helpers'

export default class AuthWindow {
private _app:Application
public logger:Logger
public window

private _authState

constructor(app:Application) {
this._app = app
this.logger = this._app.logger.extend('AuthWindow')

this.window = createWindow('main', {
width: 400,
Expand All @@ -18,15 +24,34 @@ export default class AuthWindow {
preload: path.join(__dirname, 'preload.js'),
},
})

this._app.getPlatform().api.Authentication.getAuthenticationUrl().then((authState) => {
this.logger.log('constructor()() Authentication URL received:', authState)
this._authState = authState
this.window.loadURL(authState.sisuAuth.MsaOauthRedirect)
})

session.defaultSession.webRequest.onHeadersReceived({
urls: [
'https://login.live.com/oauth20_authorize.srf?*',
'https://login.live.com/ppsecure/post.srf?*',
],
}, (details, callback) => {

const redirectUri = 'ms-xal-000000004c20a908://auth'
if(details.responseHeaders.Location !== undefined && details.responseHeaders.Location[0].includes(redirectUri)){
this.logger.log('constructor() Got redirect URI from OAUTH:', details.responseHeaders.Location[0])
this.window.close()

this._app.getPlatform().api.Authentication.authenticateUser(this._authState, details.responseHeaders.Location[0]).then((result) => {
this.logger.log('constructor() Authenticated user:', result)
})

callback({ cancel: true })
} else {
callback(details)
}
})

// if (this._app.isProduction) {
// this.window.loadURL('app://./boot')
// } else {
// const port = process.argv[2]
// this.window.loadURL(`http://localhost:${port}/boot`)
// this.window.webContents.openDevTools({
// mode: 'bottom'
// })
// }
}
}
44 changes: 43 additions & 1 deletion app/main/worker.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,57 @@
import worker from 'node:worker_threads'
import GreenlightWorker from '@greenlight/platform/src/worker'
import Logger from '@greenlight/logger'
import { AuthStore } from '@greenlight/authentication'

export class AuthenticationStore extends AuthStore {
private _msgBus:worker.MessagePort

constructor(port:worker.MessagePort) {
super()
console.log('constructor() AuthenticationStore created')

this._msgBus = port
}
load() {
console.log('load() AuthenticationStore load tokens triggered', this._msgBus)
this._msgBus.postMessage({
requestId: Math.floor(Math.random()*1000),
action: 'auth_load'
})
return true
}

save() {
console.log('save() AuthenticationStore save tokens triggered')
const tokens = JSON.stringify({
userToken: this._userToken?.data,
sisuToken: this._sisuToken?.data,
jwtKeys: this._jwtKeys,
})
this._msgBus.postMessage({
requestId: Math.floor(Math.random()*1000),
action: 'auth_save',
params: [tokens]
})
return true
}

clear() {
console.log('clear() AuthenticationStore clear tokens triggered')
return true
}
}

export default class Worker {
private _platformWorker:GreenlightWorker
public logger = new Logger('GreenlightWorker:main')
private _store

constructor() {
worker.parentPort?.once('message', (handler) => {
try {
this._platformWorker = new GreenlightWorker(this.logger, handler.port)
this._store = new AuthenticationStore(handler.port)
this._platformWorker = new GreenlightWorker(this.logger, handler.port, this._store)
this._platformWorker.once('ready', (result) => {
if(result === true)
worker.parentPort?.postMessage('ok');
Expand Down
10 changes: 6 additions & 4 deletions packages/authentication/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Xal } from 'xal-node'
import { TokenStore, Xal } from 'xal-node'
import StreamingToken from 'xal-node/dist/lib/tokens/streamingtoken'
import AuthStore from './authstore'
import Logger from '@greenlight/logger'
Expand All @@ -23,14 +23,16 @@ export interface ISisuAuthenticationResponse {

class Authentication {

private _Store = new AuthStore()
private _Xal:Xal = new Xal(this._Store)
private _Store:AuthStore
private _Xal:Xal
public logger:Logger = new Logger('Authentication')

private _xhomeToken: StreamingToken | undefined
private _xcloudToken: StreamingToken | undefined

constructor() {
constructor(store:AuthStore = new AuthStore()) {
this._Store = store
this._Xal = new Xal(this._Store)
this.logger.log('constructor() Creating new Authentication instance')
this._Store.load()
}
Expand Down
16 changes: 16 additions & 0 deletions packages/platform/src/controllers/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import GreenlightPlatform from '..'
import { AuthenticationState } from '@greenlight/authentication'

export default class Authentication {

Expand All @@ -23,4 +24,19 @@ export default class Authentication {
})
}

async getAuthenticationUrl():Promise<AuthenticationState> {
return await this._platform.sendMessage({
controller: 'Authentication',
action: 'getAuthenticationUrl'
})
}

async authenticateUser(authState:AuthenticationState, redirectUrl:string):Promise<boolean> {
return await this._platform.sendMessage({
controller: 'Authentication',
action: 'authenticateUser',
params: [authState, redirectUrl]
})
}

}
56 changes: 45 additions & 11 deletions packages/platform/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default class GreenlightPlatform {
})

this._worker.postMessage({ port: this._mainChannel.port1 }, [this._mainChannel.port1]);
this._mainChannel.port2.on('message', (value) => this.message(value));
this._mainChannel.port2.on('message', async (value) => await this.message(value));
})
}

Expand All @@ -74,30 +74,64 @@ export default class GreenlightPlatform {
})
}

message(value:WorkerMessageResponse) {
async message(value:WorkerMessageResponse|WorkerMessage) {
this.logger.log('message() Received message:', JSON.stringify(value));

if(value.requestId !== undefined){
const promise = this._messagePromiseQueue.get(value.requestId)
// If message is WorkerMessageResponse
if('responseId' in value){
const promise = this._messagePromiseQueue.get(value.responseId)
if(promise){
this._messagePromiseQueue.delete(value.requestId)
this._messagePromiseQueue.delete(value.responseId)
promise.resolve(value.data)
} else {
this.logger.error('message() Promise not found for requestId:', value.requestId)
this.logger.error('message() Promise not found for responseId:', value.responseId)
}
} else if('requestId' in value){
if(value.action == 'auth_load'){
const res = await this.onAuthLoad()
const message:WorkerMessageResponse = {
responseId: (value.requestId as number),
data: res
}
this.sendMessage(message)
} else if(value.action == 'auth_save'){
await this.onAuthSave(value.params?.[0])
} else if(value.action == 'auth_clear'){
await this.onAuthClear()
} else {
this.logger.error('message() Unknown action:', value.action)
}
}
}

onAuthLoad():any {
return false
}

onAuthSave(tokens:string) {
return false
}

onAuthClear() {
return false
}

_messagePromiseQueue = new Map<number, { resolve:Function, reject:Function }>()

sendMessage(value:WorkerMessage):Promise<any> {
sendMessage(value:WorkerMessage|WorkerMessageResponse):Promise<any> {
return new Promise((resolve, reject) => {
if(this._worker){
value.requestId = Math.floor(Math.random() * 1000)
this.logger.log('sendMessage() Sending message to worker:', JSON.stringify(value));
this._mainChannel.port2.postMessage(value)
if('responseId' in value) {
this.logger.log('sendMessage() Sending message to worker:', JSON.stringify(value));
this._mainChannel.port2.postMessage(value)
resolve(true)
} else {
value.requestId = Math.floor(Math.random() * 1000)
this.logger.log('sendMessage() Sending message to worker:', JSON.stringify(value));
this._mainChannel.port2.postMessage(value)

this._messagePromiseQueue.set(value.requestId, { resolve, reject })
this._messagePromiseQueue.set(value.requestId, { resolve, reject })
}
}
})
}
Expand Down
Loading

0 comments on commit 8956adf

Please sign in to comment.