diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index bc5e5a36f66dbf3e904bc6d61f71ed35be10c126..dcd7bead93b787dcf551cfe9ce5eb02b4d32a3f3 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,14 +1,21 @@
 import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';
 import { LauncherComponent } from './launcher/launcher.component';
-import { TokenextractorComponent } from './tokenextractor/tokenextractor.component';
+import { KeygenComponent } from './keygen/keygen.component';
+import { ConnectingComponent } from './connecting/connecting.component';
+
+// import { TokenextractorComponent } from './tokenextractor/tokenextractor.component';
 
 
 const routes: Routes = [
   { path: '', redirectTo: 'launch', pathMatch: 'full'},
   { path: 'launch', component: LauncherComponent},
-  // { path: 'sshauthz_callback', component: TokenextractorComponent}
-  { path: 'sshauthz_callback', component: LauncherComponent}
+  { path: 'finishlaunch', component: LauncherComponent},
+  { path: 'cancellaunch', component: LauncherComponent},
+  { path: 'sshauthz_callback', component: KeygenComponent},
+  { path: 'connecting', component: ConnectingComponent }
+
+  // { path: 'sshauthz_callback', component: LauncherComponent}
 
 
 ];
diff --git a/src/app/app.component.css b/src/app/app.component.css
index b7da323b68967dd24667e48616df31db78844166..31ef901f27c3196c82b8461d129518631f654d33 100644
--- a/src/app/app.component.css
+++ b/src/app/app.component.css
@@ -3,3 +3,8 @@
      Every toolbar row uses a flexbox row layout. */
   flex: 1 1 auto;
 }
+.fill-horizontal-space {
+  /* This fills the remaining space, by using flexbox.
+     Every toolbar row uses a flexbox row layout. */
+  flex: 0 1 auto;
+}
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 19425a3eab26a12d81c60763b36691f42b1b0b43..11d6bd56902f676bd2b653864c579610f669d08b 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,14 +1,5 @@
-<mat-toolbar color="primary">
-  <mat-toolbar-row>
-    <span>{{title}}</span>
-    <span class="fill-remaining-space"></span>
-      <span class="fill-remaining-space"></span>
-      <!-- <app-siteselection></app-siteselection> -->
-      <!-- <button  mat-button *ngIf="authorised" (click)="logout()">Logout</button>
-      <button  mat-button *ngIf="!authorised" (click)="login()">Login</button> -->
-    </mat-toolbar-row>
-  </mat-toolbar>
 
-<router-outlet></router-outlet>
 
-<app-teserrors></app-teserrors>
+<router-outlet class="fill-remaining-space"></router-outlet>
+
+<!-- <app-teserrors></app-teserrors> -->
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 51a81f29de18c93fcf2910491fb43fbcccafc9be..44d79aa300b303e1e04ad250b5a39655e7dfb43d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -15,20 +15,31 @@ import { MatTableModule } from '@angular/material/table';
 import { MatCardModule } from '@angular/material';
 import { MatToolbarModule } from '@angular/material';
 import { MatDialogModule, MatDialog } from '@angular/material';
+import { MatSnackBarModule } from "@angular/material";
 import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
 import { NoopAnimationsModule } from '@angular/platform-browser/animations';
 import { ComputesitesService } from './computesites.service';
 import { StrudelappsService } from './strudelapps.service';
+import { AuthorisationService } from './authorisation.service';
+
 import { TesService} from './tes.service';
 import { FormsModule } from '@angular/forms';
 import { HttpClientModule } from '@angular/common/http';
 import { JobComponent } from './job/job.component';
-import { SiteselectionComponent } from './siteselection/siteselection.component';
+// import { SiteselectionComponent } from './siteselection/siteselection.component';
 import { TeserrorsComponent } from './teserrors/teserrors.component';
 import { AppRoutingModule } from './/app-routing.module';
 import { TokenextractorComponent } from './tokenextractor/tokenextractor.component';
-import { LogindialogComponent } from './logindialog/logindialog.component';
+// import { LogindialogComponent } from './logindialog/logindialog.component';
 import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component';
+// import { LaunchdialogComponent } from './launchdialog/launchdialog.component';
+
+import { ModaldialogComponent } from './modaldialog/modaldialog.component';
+import { MatSidenavModule, MatExpansionModule, MatIconModule } from '@angular/material';
+import { StrudelapplistComponent } from './strudelapplist/strudelapplist.component';
+import { KeygenComponent } from './keygen/keygen.component';
+import { ConnectingComponent } from './connecting/connecting.component';
+
 
 
 
@@ -38,11 +49,15 @@ import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component';
     LauncherComponent,
     JoblistComponent,
     JobComponent,
-    SiteselectionComponent,
+    // SiteselectionComponent,
     TeserrorsComponent,
     TokenextractorComponent,
-    LogindialogComponent,
-    LogoutdialogComponent
+    LogoutdialogComponent,
+    ModaldialogComponent,
+    // LaunchdialogComponent,
+    StrudelapplistComponent,
+    KeygenComponent,
+    ConnectingComponent
   ],
   imports: [
   BrowserModule,
@@ -61,10 +76,14 @@ import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component';
   FormsModule,
   HttpClientModule,
   AppRoutingModule,
+  MatSidenavModule,
+  MatExpansionModule,
+  MatIconModule,
+  MatSnackBarModule,
 
   ],
-  entryComponents: [ LogindialogComponent, LogoutdialogComponent ],
-  providers: [ StrudelappsService, ComputesitesService, TesService, MatDialog],
+  entryComponents: [ ModaldialogComponent, LogoutdialogComponent,  ],
+  providers: [ StrudelappsService, ComputesitesService, TesService, MatDialog, AuthorisationService],
   bootstrap: [AppComponent]
 })
 export class AppModule { }
diff --git a/src/app/authorisation.service.ts b/src/app/authorisation.service.ts
index 017e383419c7478b7872123a0c135059a442abd4..51fa587faca021f49a1bbf46fd3e058b1fb50952 100644
--- a/src/app/authorisation.service.ts
+++ b/src/app/authorisation.service.ts
@@ -8,16 +8,24 @@ import {LocationStrategy} from '@angular/common';
 // import { keypair } from 'keypair';
 import * as keypair from 'keypair';
 import * as forge from "node-forge";
-import { Identity, AuthToken, KeyCert, AuthService } from './identity';
+import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity';
 import {BehaviorSubject} from 'rxjs/BehaviorSubject';
 import {Subject} from 'rxjs/Subject';
 import {TesService} from './tes.service';
+import { throwError } from 'rxjs';
+
+
+export class SshauthzServer {}
 
 @Injectable({
   providedIn: 'root'
 })
 export class AuthorisationService {
-  private token: Subject<AuthToken>;
+  // public token: BehaviorSubject<AuthToken>;
+  public token: AuthToken
+  public SshAuthzServers: BehaviorSubject<SshAuthzServer[]>;
+  public readyToNavigate: Subject<Boolean>;
+  public progress: Subject<string>;
   // private keyCert: Subject<KeyCert>;
 
 
@@ -26,10 +34,30 @@ export class AuthorisationService {
               private route: ActivatedRoute,
               private router: Router,
               private tesService: TesService) {
-                this.token = new Subject<AuthToken>();
-                this.token.subscribe(token => this.getCert(token));
+                console.log('created AuthorisationService');
+                // this.token = new BehaviorSubject<AuthToken>(new AuthToken('',''));
+                this.readyToNavigate = new Subject<Boolean>();
+                this.readyToNavigate.next(false);
+                this.progress = new Subject<string>();
+                this.progress.next("");
+                // this.token.subscribe(token => this.getCert(token));
                 this.route.fragment.subscribe(frag => this.storeToken(frag));
+                this.SshAuthzServers = new BehaviorSubject<SshAuthzServer[]>([]);
+                this.getSshAuthzServers();
+ }
+
+ getSshAuthzServers() {
+   let headers = new HttpHeaders();
+   let options = { headers: headers, withCredentials: false};
+   this.http.get<SshAuthzServer[]>('./assets/config/authservers.json',options)
+                    .pipe(catchError(this.handleError('getSshAuthzServers')))
+                    .subscribe(resp => this.updateSshAuthzServers(resp));
  }
+
+ updateSshAuthzServers(resp) {
+   this.SshAuthzServers.next(<SshAuthzServer[]>resp);
+ }
+
  storeToken(frag: string) {
    if (frag === undefined || frag == null) {
        return;
@@ -37,8 +65,8 @@ export class AuthorisationService {
    let tokenmatch = null;
    let statematch = null;
    if (!(frag === undefined) && !(frag == null)) {
-     tokenmatch = frag.match(/access_token\=([\S\s]*?)[&|$]/);
-     statematch = frag.match(/state\=([\S\s]*?)[&|$]/);
+     tokenmatch = frag.match(/access_token\=([^&]+)(&|$)/);
+     statematch = frag.match(/state\=([^&]+)(&|$)/);
    }
    if (tokenmatch == null || statematch == null) {
      return;
@@ -46,7 +74,7 @@ export class AuthorisationService {
 
    let accesstoken = tokenmatch[1];
    let state = statematch[1];
-   this.router.navigate(['/']);
+   // this.router.navigate(['/']);
 
    //Verify that the state matched the nonce we used when initiating login
    let tuple = JSON.parse(localStorage.getItem('authservice'));
@@ -54,14 +82,13 @@ export class AuthorisationService {
      return
    }
 
-   this.token.next(new AuthToken(tokenmatch[1],tuple[0]));
-
-   // TODO fire off a query to the auth service to get the associated sites
+   // this.token.next(new AuthToken(tokenmatch[1],tuple[0]));
+   this.token = new AuthToken(tokenmatch[1],tuple[0]);
 
 
  }
 
- getCert(token: AuthToken) {
+ public getCert(token: AuthToken) {
     console.log('in getCert');
     if (token.token === undefined || token.token === '' || token.token == null) {
       console.log('no authtoken available, we wont be able to generate a cert');
@@ -80,46 +107,65 @@ export class AuthorisationService {
     let headers = new HttpHeaders();
     let options = { headers: headers, withCredentials: true};
 
-    let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.authservice.sign};
+    let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.sshauthzservice.sign};
 
     console.log('posting to getcert',this.tesService.Base);
     this.http.post<any>(this.tesService.Base+'/getcert',data, options)
                        .pipe(catchError(this.handleError('getCert',[])))
-                       .subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.authservice))
+                       .subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.sshauthzservice),
+                                  error => this.httperrorLogout(error,token.sshauthzservice));
    console.log('getcert complete');
  }
 
- makeKeyCert(key: string, resp, authservice: AuthService) {
+
+ makeKeyCert(key: string, resp, sshauthzservice: SshAuthzServer) {
    let keyCert = new KeyCert()
    keyCert.key = key;
    keyCert.cert = resp['cert'];
-   keyCert.authservice = authservice;
    console.log('updating keycert',keyCert);
-   this.tesService.keyCert.next(keyCert);
+   this.tesService.sshAdd(keyCert);
+   // this.tesService.keyCert.next(keyCert);
+   // As soon as the certificate has been generated, we log back out of the signing server
+   if (!(sshauthzservice.logout === null)) {
+     window.open(sshauthzservice.logout);
+   }
+   this.readyToNavigate.next(true);
  }
 
- public login() {
+ public login(authservice: SshAuthzServer) {
    let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback";
    let nonce="asdfzxcv";
-   let authservice = new AuthService();
-   authservice.base = "https://autht.massive.org.au/hpcid/";
-   authservice.authorise = authservice.base + 'oauth/authorize';
-   authservice.sign = authservice.base + 'api/v1/sign_key';
-   authservice.client_id = "86c06039-e589-4b39-9d1f-9eca431be18f";
    localStorage.setItem('authservice', JSON.stringify([authservice,nonce]));
-   window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id);
+   if (authservice.scope == null) {
+     window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id);
+   } else {
+     window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id+"&scope="+authservice.scope);
+
+   }
  }
 
  private httperror(error: any) {
+   this.tesService.statusMsg.next(error);
    console.log(error);
  }
+ private httperrorLogout(error: any,sshauthzservice: SshAuthzServer) {
+   this.tesService.statusMsg.next(error);
+   console.log(error);
+   if (!(sshauthzservice.logout === null)) {
+     window.open(sshauthzservice.logout);
+   }
+ }
 
  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      console.log('in handle error',operation);
      console.log(error.status);
      console.error(error);
-     return of(result as T);
+     if (error.status == 500) {
+       return throwError("The authorisation server encountered and error. Please try again in a few minutes")
+     }
+     return throwError(error.message);
+     // return of(result as T);
    };
  }
 }
diff --git a/src/app/computesite.ts b/src/app/computesite.ts
index a6de7f31281d5d7bc47f25b869330dcd4f4ae111..695f35545aad8b8d4bbab933a6ab507c019d037b 100644
--- a/src/app/computesite.ts
+++ b/src/app/computesite.ts
@@ -1,3 +1,5 @@
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+
 export class Computesite {
   url: string;  // The URL runs a web service to help construct sbatch commands
                 // Infact the URL should return the entire interface for
@@ -7,7 +9,9 @@ export class Computesite {
   cafingerprint: string;  // Certificates contain a CA fingerprint. We use this
                           // to figure out which compute site a certificate is valid
                           // for
-  appCatalog: Strudelapp[];
+  appCatalog: BehaviorSubject<Strudelapp[]>;
+  appCatalogUri: string;
+
 }
 
 export class Strudelapp {
diff --git a/src/app/computesites.service.ts b/src/app/computesites.service.ts
index 219535ae8932eb8a2263ed4e2b57ddb6b8b27625..4577d558fb97fa8663a4d4456956edfa04c69e64 100644
--- a/src/app/computesites.service.ts
+++ b/src/app/computesites.service.ts
@@ -1,17 +1,67 @@
 import { Injectable } from '@angular/core';
-import { Computesite } from './computesite';
-import { COMPUTESITES } from './mock-compute-site';
+import { Computesite, Strudelapp } from './computesite';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+import { of } from 'rxjs/observable/of';
+import { catchError, map, tap } from 'rxjs/operators';
+
+
 
 @Injectable({
   providedIn: 'root',
 })
 export class ComputesitesService {
+  public computesites: BehaviorSubject<Computesite[]>;
+
+  constructor(private http: HttpClient,) {
+    this.computesites = new BehaviorSubject<Computesite[]>([]);
+    this.computesites.subscribe(computesites => this.getStrudelApps(computesites))
+    this.getComputeSites();
+  }
 
-  constructor() { }
+  getStrudelApps(computesites: Computesite[]) {
+    for (let s of computesites) {
+      let headers = new HttpHeaders();
+      let options = { headers: headers, withCredentials: false};
+      this.http.get<Strudelapp[]>(s.appCatalogUri,options)
+                       .pipe(catchError(this.handleError('getStrudelApps')))
+                       .subscribe(resp => this.updateStrudelApps(s.appCatalog,resp));
+    }
 
-  getComputeSites(): Computesite[] {
-    return COMPUTESITES;
   }
 
+  private handleError<T> (operation = 'operation', result?: T) {
+    return (error: any): Observable<T> => {
+      console.error(error);
+      return of(result as T);
+    };
+  }
+
+  updateStrudelApps(appCatalog: BehaviorSubject<Strudelapp[]>,apps) {
+    appCatalog.next(<Strudelapp[]>apps);
+  }
+
+  getComputeSites() {
+    let headers = new HttpHeaders();
+    let options = { headers: headers, withCredentials: false};
+    this.http.get<Strudelapp[]>('./assets/config/computesites.json',options)
+                     .pipe(catchError(this.handleError('getComputeSites')))
+                     .subscribe(resp => this.updateComputeSites(resp));
+  }
+
+  updateComputeSites(resp) {
+    var computesites: Computesite[] = []
+    for (let cs of resp) {
+      let computesite = <Computesite>cs;
+      computesite.appCatalog = new BehaviorSubject<Strudelapp[]>([])
+      computesites.push(computesite);
+    }
+    this.computesites.next(computesites);
+  }
+
+  returnComputeSites(): Computesite[] {
+    return this.computesites.value;
+  }
 
 }
diff --git a/src/app/identity.ts b/src/app/identity.ts
index 952370b7daa3aac0ee69baffe62ff09ab01dcc40..63a079b7e96690fbedc7a234f463ac52a61f1a4e 100644
--- a/src/app/identity.ts
+++ b/src/app/identity.ts
@@ -4,37 +4,52 @@ import {Computesite} from './computesite';
 export class Identity {
   username: string;
   site: Computesite;
-  authservice: AuthService;
+  authservice: SshAuthzServer;
   constructor( username: string, site: Computesite) {
     this.username = username;
     this.site = site;
   }
+
+  copy_skip_catalog(): Identity {
+    let id = new Identity(null,null);
+    id.username = this.username;
+    id.site = new Computesite();
+    id.site.url = this.site.url;
+    id.site.host = this.site.host;
+    id.site.name = this.site.name;
+    id.site.cafingerprint = this.site.cafingerprint;
+    return id;
+  }
   displayName(): string {
     return this.username+'@'+this.site.name;
   }
   repr(): string {
-    return JSON.stringify([this.username,this.site.cafingerprint,this.site.host]);
+    return JSON.stringify([this.username,this.site.cafingerprint,this.site.host,this.site.url]);
   }
 }
 
 export class AuthToken {
   token: string;
-  authservice: AuthService;
-  constructor( token: string, authservice: AuthService ) {
+  sshauthzservice: SshAuthzServer;
+  constructor( token: string, sshauthzservice: SshAuthzServer ) {
     this.token = token;
-    this.authservice = authservice;
+    this.sshauthzservice = sshauthzservice;
   }
 }
 
 export class KeyCert {
   key: string;
   cert: string;
-  authservice: AuthService;
+  // authservice: SshAuthzServer;
 }
 
-export class AuthService {
+export class SshAuthzServer {
   base: string;
   authorise: string;
   sign: string;
   client_id: string;
+  name: string;
+  icon: string;
+  scope: string;
+  logout: string;
 }
diff --git a/src/app/job/job.component.html b/src/app/job/job.component.html
index 68466604262e97d37c032f36cf332bf2463fefde..2fbde39f3d096b478da7cd8abd5c29b2f9cfb845 100644
--- a/src/app/job/job.component.html
+++ b/src/app/job/job.component.html
@@ -9,9 +9,11 @@
         {{ jobdata.state }}
       </td>
       <td withdt="10%">
-        <button mat-button (click)="onCancel()">
-          Cancel
-        </button>
+        <div *ngIf="!nocancel">
+          <button mat-button (click)="onCancel()" >
+            Cancel
+          </button>
+        </div>
       </td>
       <td width="10%">
         <div *ngIf="available">
diff --git a/src/app/job/job.component.ts b/src/app/job/job.component.ts
index c1518a7895e6349e50531b9a9ec8362ee24d3936..0998980c1c0ec3e073a184bfb1f521c8b6da588a 100644
--- a/src/app/job/job.component.ts
+++ b/src/app/job/job.component.ts
@@ -13,33 +13,36 @@ export class JobComponent implements OnInit {
   @Input() jobdata: Job;
   public available: Boolean;
   private busy: Boolean;
+  public nocancel: Boolean;
   constructor(private tesService: TesService, private strudelAppsService: StrudelappsService) {
 
 
 }
 
   ngOnInit() {
-    console.log('creating job component');
     if (this.jobdata.state == "RUNNING") {
       this.available = true;
     } else {
       this.available = false;
     }
     this.tesService.busy.subscribe(busy => this.busy = busy);
-    console.log('creating job component complete');
+    if (this.jobdata.app.startscript === null) {
+      this.nocancel = true;
+    } else {
+      this.nocancel = false;
+    }
 
   }
 
   onCancel() {
-    this.jobdata.app = this.strudelAppsService.getApp(this.jobdata.name);
+    // this.jobdata.app = this.strudelAppsService.getApp(this.jobdata.name);
     console.log(this.jobdata);
     this.tesService.cancel(this.jobdata);
   }
 
   onConnect() {
-    console.log('attempting connect');
     // Before connecting we must resolve what type of app we are connecting to
-    this.jobdata.app = this.strudelAppsService.getApp(this.jobdata.name);
+    // this.jobdata.app = this.strudelAppsService.getApp(this.jobdata.name);
     this.tesService.connect(this.jobdata);
   }
 
diff --git a/src/app/joblist/joblist.component.css b/src/app/joblist/joblist.component.css
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0c6efabb438ac61b93bb5959301e4e760b7fe8d8 100644
--- a/src/app/joblist/joblist.component.css
+++ b/src/app/joblist/joblist.component.css
@@ -0,0 +1,9 @@
+html, body {
+  height: 100%;
+}
+
+.wrapper {
+  /* display: flex;
+  flex-direction: column; */
+  height: 100%;
+}
diff --git a/src/app/joblist/joblist.component.html b/src/app/joblist/joblist.component.html
index 0ba48dc7f3b06feb2394db0dd5c6b2691cf58948..ac77ade48a30748d94588678be1eec298f5bfee1 100644
--- a/src/app/joblist/joblist.component.html
+++ b/src/app/joblist/joblist.component.html
@@ -1,16 +1,17 @@
-<div *ngIf="identities.length == 0">
+<!-- <div *ngIf="identities.length == 0">
   <mat-card>
     Click identity and login to a service to start an application.
   </mat-card>
 </div>
-<div *ngFor="let id of identities">
-  <mat-card>
-    <mat-card-title>{{ id.displayName() }}</mat-card-title>
-    <div *ngIf="jobs[id.repr()] == undefined || jobs[id.repr()].length == 0 ">
-      No jobs running yet
+<div *ngFor="let id of identities"> -->
+
+  <mat-card class="fill-remaining-space">
+    <div *ngIf="identity == undefined" >
+      Select your username an compute site on the left to see running jobs or start new jobs.
+    </div>
+    <div *ngIf="!(identity == undefined)">
+      <div *ngFor="let job of jobs[identity.repr()]">
+        <app-job [jobdata]=job></app-job>
+      </div>
     </div>
-  <div *ngFor="let job of jobs[id.repr()]">
-    <app-job [jobdata]=job></app-job>
-  </div>
 </mat-card>
-</div>
diff --git a/src/app/joblist/joblist.component.ts b/src/app/joblist/joblist.component.ts
index f27d908f909b05e591b51deb1789a4541c82611e..5f2d2f14cecf9f5c38a1f4d4905c5d80c4bde903 100644
--- a/src/app/joblist/joblist.component.ts
+++ b/src/app/joblist/joblist.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Input } from '@angular/core';
 import {TesService} from '../tes.service';
 import { Job } from '../job';
 import { Observable } from 'rxjs/Observable';
@@ -11,21 +11,23 @@ import { Identity } from '../identity';
   templateUrl: './joblist.component.html',
   styleUrls: ['./joblist.component.css']
 })
+
 export class JoblistComponent implements OnInit {
+  @Input() identity: Identity;
   public jobs: {[id: string]: Job[] } = {};
   public identities: Identity[];
   private displayedColumns = ['id'];
   private jobsSubscription: any;
   private idSubscription: any;
 
+
   constructor(private tesService: TesService,) {
 }
 
   ngOnInit() {
-    console.log('creating joblist component');
     this.jobsSubscription = this.tesService.joblist.subscribe(jobs => this.updateJobs(jobs));
-    this.idSubscription = this.tesService.identities.subscribe(ids => this.updateIds(ids));
-    console.log("joblist component complete");
+    // this.idSubscription = this.tesService.identities.subscribe(ids => this.updateIds(ids));
+
   }
 
   public ngOnDestroy(): void {
@@ -34,18 +36,19 @@ export class JoblistComponent implements OnInit {
       }
 
   }
-  updateIds(identities: Identity[]) {
-    console.log('update identities');
-    this.identities = identities;
-    console.log('update identities complete');
-
-  }
+  // updateIds(identities: Identity[]) {
+  //   this.identities = identities;
+  // }
 
   updateJobs(jobs) {
-    console.log('updating jobs')
+    console.log('update jobs',jobs);
     this.jobs = jobs;
-    console.log('update jobs complete')
+
   }
 
+  // setId(identity) {
+  //   this.identity = identity;
+  // }
+
 
 }
diff --git a/src/app/launcher/launcher.component.css b/src/app/launcher/launcher.component.css
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..67e39868343ed94b7f23e6a1504f5879721f9e57 100644
--- a/src/app/launcher/launcher.component.css
+++ b/src/app/launcher/launcher.component.css
@@ -0,0 +1,10 @@
+.wrapper {
+  /* display: flex;
+  flex-direction: column; */
+  flex: 1 1 auto;
+}
+.vspace {
+  /* display: flex;
+  flex-direction: column; */
+height: 500px;
+}
diff --git a/src/app/launcher/launcher.component.html b/src/app/launcher/launcher.component.html
index 5ad7440af8fb65de1127d07f27c11bd53bf6ec7f..0315cba11f0811081f42b79d9499f4d6e8bab209 100644
--- a/src/app/launcher/launcher.component.html
+++ b/src/app/launcher/launcher.component.html
@@ -1,27 +1,47 @@
+<mat-toolbar color="primary">
+  <mat-toolbar-row>
+    <button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button>
 
-<mat-form-field>
-	<mat-select placeholder="Identity" (selectionChange)="selectId($event)">
-		<mat-option *ngFor="let id of identities" [value]=id>
-			{{id.displayName()}}
-		</mat-option>
-		<mat-option>
-				<button mat-button (click)=login()>Login ... </button>
-		</mat-option>
-		<mat-option>
-			<button mat-button (click)=logout()> Logout </button>
-		</mat-option>
+    <span>Strudel v2.0</span>
+    <span class="fill-horizontal-space"></span>
+    </mat-toolbar-row>
+  </mat-toolbar>
 
-	</mat-select>
-</mat-form-field>
-<mat-form-field>
-	<mat-select placeholder="Application" [(value)]=app>
-		<mat-option *ngFor="let a of strudelapps" [value]=a>
-			{{a.name}}
-		</mat-option>
-	</mat-select>
-</mat-form-field>
-<button mat-button (click)=configureResources() [disabled]="!app || !identity">Configure Resources</button>
-<!-- <button mat-button (click)=configureApp() [disabled]="!app || !identity">Configure App</button> -->
-<button mat-button (click)=submitApp() [disabled]="!app || !identity">Start</button>
+<mat-sidenav-container autosize class="fill-remaining-space">
+  <mat-sidenav #idSideNav mode="side" opened>
+		<mat-accordion>
+			<mat-expansion-panel (click)=selectId(undefined)>
+				<mat-expansion-panel-header>
+          <mat-panel-title>
+          Login
+        </mat-panel-title>
+				</mat-expansion-panel-header>
+        <div *ngFor="let sshauthzserver of sshauthzservers">
+					<button mat-button (click)=login(sshauthzserver)>Login to {{ sshauthzserver.name }} </button>
+        </div>
+				<button mat-button (click)=logout()>Logout</button>
+			</mat-expansion-panel>
+			<div *ngFor="let id of identities">
+				<mat-expansion-panel>
+					<mat-expansion-panel-header>
+						{{ id.displayName() }}
+					</mat-expansion-panel-header>
+					<!-- <div *ngFor="let app of id.site.appCatalog.value">
+            <div *ngIf="app.startscript != null">
+						  <button mat-button (click)=openLaunchWindow(app,id)>{{ app.name }}</button>
+            </div>
+					</div> -->
+          <app-strudelapplist [applist]=id.site.appCatalog [identity]=id></app-strudelapplist>
+				</mat-expansion-panel>
+			</div>
+	</mat-accordion>
 
-<app-joblist></app-joblist>
+	</mat-sidenav>
+  <mat-sidenav-content>
+		<app-joblist [identity]=identity></app-joblist>
+    <div class="vspace"></div>
+
+	</mat-sidenav-content>
+</mat-sidenav-container>
+
+<!-- <app-joblist></app-joblist> -->
diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts
index cb7a825c264ec5a3a15b45c134f9d354f8e511f0..de59d5705b684744a08922a61619755118bbaa71 100644
--- a/src/app/launcher/launcher.component.ts
+++ b/src/app/launcher/launcher.component.ts
@@ -1,12 +1,23 @@
-import { Component, OnInit, NgModule, Inject } from '@angular/core';
+import { Component, OnInit, NgModule, Inject, Input } from '@angular/core';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogConfig } from '@angular/material';
+import { Location } from '@angular/common';
+// import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+import { fromEvent } from 'rxjs';
+import { timer } from 'rxjs/observable/timer';
+import { repeat } from 'rxjs/operators';
+
 import {Strudelapp} from '../strudelapp';
 import { StrudelappsService } from '../strudelapps.service';
-import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
 import { TesService } from '../tes.service';
+import { AuthorisationService } from '../authorisation.service';
 import { Identity } from '../identity';
 import { Computesite } from '../computesite';
 import { LogoutdialogComponent } from '../logoutdialog/logoutdialog.component';
-import { LogindialogComponent } from '../logindialog/logindialog.component'
+// import { LogindialogComponent } from '../logindialog/logindialog.component';
+// import { LaunchdialogComponent } from '../launchdialog/launchdialog.component';
+import { BatchInterface} from '../batchinterface';
+import { SshAuthzServer } from '../identity';
 
 
 
@@ -17,36 +28,60 @@ import { LogindialogComponent } from '../logindialog/logindialog.component'
 })
 export class LauncherComponent implements OnInit {
   public strudelapps: Strudelapp[];
+  @Input() menuopen: boolean;
 
   public app: Strudelapp;
   public authorised: boolean;
   public identity: Identity;
   public identities: Identity[];
-
-  constructor( public dialog: MatDialog, private tesService: TesService) {
+  public sshauthzservers: SshAuthzServer[];
+  private launchwindow: any;
+  private launchwindowWatcher: any;
+
+  constructor( public dialog: MatDialog,
+                private tesService: TesService,
+                private authService: AuthorisationService) {
+    authService.SshAuthzServers.subscribe(o => {this.sshauthzservers = o});
+    this.launchwindowWatcher = null;
   }
 
   ngOnInit() {
-    console.log('initialising launcher compoenent');
     this.strudelapps = [];
-    setTimeout( () => { this.tesService.identities.subscribe(identities => this.updateIdentities(identities)); });
-    console.log('initialising launcher compoenent complete');
+    this.identities = [];
+    this.tesService.identities.subscribe(identities => this.updateIdentities(identities));
+    // setTimeout( () => { this.tesService.identities.subscribe(identities => this.updateIdentities(identities)); });
+    setTimeout( () => this.tesService.getIdentities() )
+  }
 
+  compareFn = (o1, o2) => {
+    // Funny "=>" function syntax gives me access to "this" as LauncherComponent
+    // otherwise "this" would be set to the mat-select object.
+    if (o2 === undefined) {
+
+      if (o1 === this.identity) {
+        return true;
+      }
+    }
+    if (o1 === o2) {
+      return true;
+    }
+    return false;
   }
 
   updateIdentities(identities) {
-    console.log('updateIdentities in launcher');
     this.identities = identities;
-    console.log('updateIdentities in launcher complete');
-
+    console.log(this.identities);
+    if (this.identities.length > 0) {
+      this.identity = this.identities[0];
+      // this.identity.site.appCatalog.subscribe(resp => this.updateApps());
+      // this.updateApps();
+    } else {
+      this.strudelapps = [];
+    }
   }
 
-login () {
-    let dialogRef = this.dialog.open(LogindialogComponent, {
-      width: '250px',
-      height: '400px',
-    });
-    return;
+login (sshauthzserver) {
+  this.authService.login(sshauthzserver);
 }
 
 logout() {
@@ -54,60 +89,28 @@ logout() {
       width: '250px',
       height: '400px',
     });
-    // this.tesService.logout();
-    // return;
 }
 
+  private buildParams(app: Strudelapp, identity: Identity, redirect): string {
+    let params = new URLSearchParams();
+    let id = identity.copy_skip_catalog();
+    id.site.appCatalog = null;
 
+    params.set('app',JSON.stringify(app));
+    params.set('identity',JSON.stringify(id));
+    params.set('redirect',redirect);
+    return params.toString();
+  }
 
 
-  selectId(event: any) {
-    console.log('in selectID');
-    if (!(event.value === undefined) && event.value instanceof(Identity)) {
-      this.identity=event.value;
-      this.strudelapps = this.identity.site.appCatalog;
-    }
-    console.log('selectID complete');
+
+  selectId(id: Identity) {
+    this.identity=id;
   }
 
+
   configureResources() {
     let configwindow = window.open(this.identity.site.url+'configure/'+this.app.name);
   }
 
-  submitApp() {
-    this.tesService.getconfig(this.app, this.identity)
-      .subscribe(resp => {console.log(resp); this.tesService.submit(this.app,this.identity,resp)});
-  }
-
-
-
-  // openDialog(msg: string ): void {
-  //     let dialogRef = this.dialog.open(LoginDialog, {
-  //       width: '250px',
-  //       height: '400px',
-  //       data: { 'msg': msg }
-  //     });
-  //
-  //     dialogRef.afterClosed().subscribe(result => {
-  //       console.log('The dialog was closed');
-  //     });
-  //   }
-
-
 }
-
-// @Component({
-//   selector: 'dialog-placeholder',
-//   templateUrl: 'dialog-placeholder.html',
-// })
-// export class LoginDialog {
-//
-//   constructor(
-//     public dialogRef: MatDialogRef<LoginDialog>,
-//     @Inject(MAT_DIALOG_DATA) public data: any) { }
-//
-//   onNoClick(): void {
-//     this.dialogRef.close();
-//   }
-//
-// }
diff --git a/src/app/logindialog/logindialog.component.css b/src/app/logindialog/logindialog.component.css
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/logindialog/logindialog.component.html b/src/app/logindialog/logindialog.component.html
deleted file mode 100644
index e83844b46f0259e81a61c6f3050447f9135e9093..0000000000000000000000000000000000000000
--- a/src/app/logindialog/logindialog.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-  <button  mat-button  (click)="onLogin()">Login to M3</button>
-  <button  mat-button  (click)="onCancel()">Cancel</button>
diff --git a/src/app/logindialog/logindialog.component.spec.ts b/src/app/logindialog/logindialog.component.spec.ts
deleted file mode 100644
index 39047f400a5544578aec85f0fd27ce4572fa6694..0000000000000000000000000000000000000000
--- a/src/app/logindialog/logindialog.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { LogindialogComponent } from './logindialog.component';
-
-describe('LogindialogComponent', () => {
-  let component: LogindialogComponent;
-  let fixture: ComponentFixture<LogindialogComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ LogindialogComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(LogindialogComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/logindialog/logindialog.component.ts b/src/app/logindialog/logindialog.component.ts
deleted file mode 100644
index 28bf80fa0eda1a5094efe40e0e003d881f0ff789..0000000000000000000000000000000000000000
--- a/src/app/logindialog/logindialog.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Component, OnInit, Inject  } from '@angular/core';
-import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
-import { TesService } from '../tes.service';
-import {AuthorisationService} from '../authorisation.service';
-
-
-
-@Component({
-  selector: 'app-logindialog',
-  templateUrl: './logindialog.component.html',
-  styleUrls: ['./logindialog.component.css']
-})
-export class LogindialogComponent implements OnInit {
-
-  constructor(
-    public dialogRef: MatDialogRef<LogindialogComponent>,
-    @Inject(MAT_DIALOG_DATA) public data: any,
-    private tesService: TesService,
-    private authService: AuthorisationService ) {
-   }
-
-  ngOnInit() {
-  }
-
-  onLogin() {
-    this.authService.login();
-  }
-  onCancel() {
-    this.dialogRef.close();
-  }
-
-
-}
diff --git a/src/app/mock-compute-site.ts b/src/app/mock-compute-site.ts
deleted file mode 100644
index 26e6050286447c57728bd45d358956180aec0754..0000000000000000000000000000000000000000
--- a/src/app/mock-compute-site.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Computesite } from './computesite';
-import { STRUDELAPPS } from './mock-strudel-app';
-
-export const COMPUTESITES: Computesite[] = [
-  { url: 'https://vm-118-138-240-255.erc.monash.edu.au/m3siteconfig/', name: 'M3',
-  host: 'm3.massive.org.au',
-  cafingerprint: 'RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I',
-  appCatalog: STRUDELAPPS }
-];
diff --git a/src/app/mock-strudel-app.ts b/src/app/mock-strudel-app.ts
deleted file mode 100644
index 3feadbf0c0fbb99017b424f16a797822f672aa69..0000000000000000000000000000000000000000
--- a/src/app/mock-strudel-app.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { Strudelapp } from './computesite';
-
-const tmuxscript = `#!/bin/bash
-#SBATCH -J tmux
-tmux new-session -d -s $SLURM_JOB_NAME bash
-# determine the process id of the tmux server
-pid=$( /bin/ps x | /bin/grep -i "[t]mux new-session -d -s" | sed 's/^\ *//' | cut -f 1 -d " " )
-ps x
-# Sleep until the tmux server exits
-while [ -e /proc/$pid ]; do sleep 5; done
-`
-
-const jupyterscript = `#!/bin/bash
-#SBATCH -J Jupyter
-/projects/pMOSP/chines/jupyter-venv/bin/jupyter-notebook
-`
-
-const stddesktop = `#!/bin/bash
-#SBATCH --job-name=desktop
-# --exclusive allows the job to consume all resources on the node reguardless of how many cpus/gpus/memory/etc
-#SBATCH --exclusive
-#SBATCH --nodes=1
-#SBATCH --ntasks=3
-#SBATCH --cpus-per-task=1
-#SBATCH --gres=gpu:K1:1
-#SBATCH --partition=m3f
-#SBATCH --mem=10288M
-
-#module purge
-module load tigervnc/1.8.0
-#module list
-export PATH=$PATH:$XMASSIVESERVICEPATH/bin
-export XDG_CONFIG_DIRS=$XMASSIVESERVICEPATH/xdg_config
-export XDG_DATA_DIRS=$XMASSIVESERVICEPATH/xdg_data:/usr/share:/usr/local/share
-echo " Starting VNC..."
-#Remove old pid files that should not exist due to vncserver not being shut down cleanly last time
-#rm -f ~/.vnc/$HOSTNAME:1.pid
-#vncserver -xstartup $XSTARTUP #:1 so we can start multiple VNC servers
-vncserver -xstartup /usr/local/desktop/services/massive-std/xstartup/xstartup
-xinit /usr/bin/xterm -- -sharevts -novtswitch -config xorg.conf &
-#vncserver
-while true;
-do
-    sleep 30;
-done`
-
-const vncviewer = { 'cmd': ['python','vncviewer.py','--password','{password}','--host','localhost','--port','{localtunnelport}'], 'redir':'?password={password}'}
-
-const tmuxclient = {'cmd': ['/usr/bin/gnome-terminal', '--','ssh','-t',
-                            'localhost','-p','{localtunnelport}','-o',
-                            'StrictHostkeyChecking=No','/bin/tmux a {sessionname}'],
-                            'redir': null}
-
-
-const jupyterclient = {'cmd': null, 'redir': '?token={token}'}
-
-export const STRUDELAPPS: Strudelapp[] = [
-  // { url: null, name: 'tmux', startscript: tmuxscript,
-  //   paramscmd: '/home/chines/smuxparams.py', client: tmuxclient, localbind: false },
-  { url: null, name: 'Jupyter Notebook', startscript: jupyterscript,
-    paramscmd: '/home/chines/jupyter_params.py', client: jupyterclient, localbind: true },
-  // { url: null, name: 'Standard Desktop', startscript: stddesktop,
-  //   paramscmd: '/home/chines/desktop_params.py', client: vncviewer, localbind: false },
-];
diff --git a/src/app/siteselection/siteselection.component.css b/src/app/siteselection/siteselection.component.css
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/app/siteselection/siteselection.component.html b/src/app/siteselection/siteselection.component.html
deleted file mode 100644
index 06dbdec83763708076be078b2e4440cb4d223ab8..0000000000000000000000000000000000000000
--- a/src/app/siteselection/siteselection.component.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-  <div>
-  <mat-form-field>
-  	<mat-select placeholder="Site" [(value)]=siteurl>
-  		<mat-option *ngFor="let site of computesites" [value]=site.url>
-  			{{site.name}}
-  		</mat-option>
-  	</mat-select>
-  </mat-form-field>
-  <div *ngIf="siteurl">
-  <!--<button mat-button (click)=configureResources()>Configure Resources</button>-->
-  </div>
-</div>
diff --git a/src/app/siteselection/siteselection.component.spec.ts b/src/app/siteselection/siteselection.component.spec.ts
deleted file mode 100644
index d77e66690e21160c5610676356ae43a9f6cf1cff..0000000000000000000000000000000000000000
--- a/src/app/siteselection/siteselection.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { SiteselectionComponent } from './siteselection.component';
-
-describe('SiteselectionComponent', () => {
-  let component: SiteselectionComponent;
-  let fixture: ComponentFixture<SiteselectionComponent>;
-
-  beforeEach(async(() => {
-    TestBed.configureTestingModule({
-      declarations: [ SiteselectionComponent ]
-    })
-    .compileComponents();
-  }));
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(SiteselectionComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/siteselection/siteselection.component.ts b/src/app/siteselection/siteselection.component.ts
deleted file mode 100644
index 0f4408b546fc2007fed5c5e451fd2fe86901eb31..0000000000000000000000000000000000000000
--- a/src/app/siteselection/siteselection.component.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { Computesite } from '../computesite'
-import { ComputesitesService} from '../computesites.service';
-
-@Component({
-  selector: 'app-siteselection',
-  templateUrl: './siteselection.component.html',
-  styleUrls: ['./siteselection.component.css']
-})
-export class SiteselectionComponent implements OnInit {
-  public computesites: Computesite[];
-  public siteurl: String;
-  constructor(private computesiteSerivce: ComputesitesService) { }
-
-  ngOnInit() {
-    this.loadComputesites();
-  }
-  configureResources() {
-    console.log('configuring resources for',this.siteurl);
-  }
-  siteselect(a: any) {
-    console.log('siteselect')
-  }
-
-  loadComputesites() {
-    this.computesites = this.computesiteSerivce.getComputeSites()
-  }
-
-}
diff --git a/src/app/strudelapp.ts b/src/app/strudelapp.ts
index 8cc0a737e87896a44f84457ec52993f2d4e38235..59b85b084e3200891ba048417af0f70d96f1f57f 100644
--- a/src/app/strudelapp.ts
+++ b/src/app/strudelapp.ts
@@ -9,4 +9,5 @@ export class Strudelapp {
   localbind: boolean; // does the application bind to a port on the localhost
                       // interface or on all interfaces. This behaviour determins
                       /// how we create tunnels
+  applist: Strudelapp[] = null;
 }
diff --git a/src/app/strudelapps.service.ts b/src/app/strudelapps.service.ts
index 30febe9ce486612064be440ac2aba39bdef66393..e6ed43f4390d6924846f3dd29627dbe208b6a272 100644
--- a/src/app/strudelapps.service.ts
+++ b/src/app/strudelapps.service.ts
@@ -1,6 +1,5 @@
 import { Injectable } from '@angular/core';
 import { Strudelapp } from './strudelapp';
-import { STRUDELAPPS } from './mock-strudel-app';
 
 @Injectable({
   providedIn: 'root',
@@ -8,12 +7,29 @@ import { STRUDELAPPS } from './mock-strudel-app';
 export class StrudelappsService {
 
   constructor() { }
-  getStrudelapps(): Strudelapp[] {
-    return STRUDELAPPS;
-  }
 
-  getApp(name: string) {
-    return STRUDELAPPS[0];
+  // getApp(name: string,applist: Strudelapp[]): Strudelapp {
+  getApp(name: string,applist: any): any {
+
+    var idx: number;
+    var app: any;
+    var sapp: Strudelapp;
+    app = applist[0];
+    for ( let item of applist) {
+      sapp = <Strudelapp>item;
+      idx = sapp.name.toLowerCase().indexOf(name.toLowerCase())
+      if (idx == 0) {
+        app = item;
+        return app;
+      }
+      if (sapp.applist != null) {
+        app = this.getApp(name,sapp.applist);
+        if (app != null) {
+          return app;
+        }
+      }
+    }
+    return null;
   }
 
 }
diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts
index fd9b487261dcaa965e122480534f5889724b5e7f..2c661057e70dadc5f6d56a44788d4886d441181f 100644
--- a/src/app/tes.service.ts
+++ b/src/app/tes.service.ts
@@ -1,55 +1,86 @@
 import { Injectable } from '@angular/core';
 import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
 import { Observable } from 'rxjs/Observable';
+import { fromEvent, throwError } from 'rxjs';
 import { of } from 'rxjs/observable/of';
 import { catchError, map, tap } from 'rxjs/operators';
 import { Job } from './job';
 import {BehaviorSubject} from 'rxjs/BehaviorSubject';
-import {Subject} from 'rxjs/Subject';
+import { Subject } from 'rxjs/Subject';
 import { Strudelapp } from './strudelapp';
 import { Computesite } from './computesite';
-import { Identity, AuthToken, KeyCert, AuthService } from './identity';
+import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity';
 import { BatchInterface} from './batchinterface';
 import {ComputesitesService} from './computesites.service';
 import { StrudelappsService } from './strudelapps.service';
 import { timer } from 'rxjs/observable/timer';
 import { repeat } from 'rxjs/operators';
-import {LocationStrategy} from '@angular/common';
+import {LocationStrategy, Location} from '@angular/common';
 import { ActivatedRoute, Router } from '@angular/router';
+import { ModaldialogComponent } from './modaldialog/modaldialog.component';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
+import { MatSnackBar } from '@angular/material';
+
+
+
 
 @Injectable({
   providedIn: 'root',
 })
 export class TesService {
-public Base='https://vm-118-138-240-255.erc.monash.edu.au/tes'
+// public Base='https://vm-118-138-240-255.erc.monash.edu.au/tes'
+public Base='http://localhost:5000';
 public statusMsg: BehaviorSubject<any>;
 public jobs: any[];
 public busy: BehaviorSubject<boolean> ;
 public joblist: BehaviorSubject<{ [id: string ]: Job[]}>;
 private timerSubscription: any;
-public keyCert: Subject<KeyCert>;
+// public keyCert: Subject<KeyCert>;
 public identities: BehaviorSubject<Identity[]>;
-private batchinterface: {[id: string] : BatchInterface};
+private appwindow: any;
+private appwindowWatcher: any;
+public batchinterface: {[id: string] : BatchInterface};
+private snackBarRef: any;
 
   constructor(private http: HttpClient,
+              public dialog: MatDialog,
               private computesite: ComputesitesService,
-              private strudelappsService: StrudelappsService) {
+              private strudelappsService: StrudelappsService,
+              public snackBar: MatSnackBar,
+              private location: Location ) {
     this.statusMsg = new BehaviorSubject<any>('');
+    this.statusMsg.subscribe(msg => this.displayMessage(msg));
     this.busy = new BehaviorSubject<boolean>(false);
     this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({});
     this.timerSubscription = null;
-    this.keyCert = new Subject<KeyCert>();
+    this.appwindowWatcher = null;
+    // this.keyCert = new Subject<KeyCert>();
     this.identities= new BehaviorSubject<Identity[]>([]);
-    this.keyCert.subscribe(keyCert => this.sshAdd(keyCert));
+    // this.keyCert.subscribe(keyCert => this.sshAdd(keyCert));
     this.batchinterface = {};
-    this.getIdentities();
+    // this.getIdentities();
+ }
+
+  private displayMessage(msg) {
+   if (msg === null || msg == "") {
+     console.log('attempting to dismis snackbar');
+     if (this.snackBarRef != undefined) {
+       this.snackBarRef.dismiss()
+     }
+   } else {
+     this.snackBarRef = this.snackBar.open(msg,'Dismiss');
+   }
  }
 
  private buildParams(app: Strudelapp, identity: Identity, batchinterface: BatchInterface): string {
    let params = new URLSearchParams();
+   let params2 = new URLSearchParams();
+   let id = identity.copy_skip_catalog();
+   id.site.appCatalog = null;
+
    params.set('app',JSON.stringify(app));
    params.set('interface',JSON.stringify(batchinterface));
-   params.set('identity',JSON.stringify(identity));
+   params.set('identity',JSON.stringify(id));
    return params.toString();
  }
 
@@ -58,18 +89,16 @@ private batchinterface: {[id: string] : BatchInterface};
    let alljobs = this.joblist.value;
    let i = 0;
    for (let j of joblist) {
-     j.app = this.strudelappsService.getApp(j.name);
+     j.app = this.strudelappsService.getApp(j.name,identity.site.appCatalog.value);
      j.identity = identity;
    }
    alljobs[identity.repr()] = joblist;
    this.joblist.next(alljobs);
-   this.statusMsg.next(null);
  }
 
  getJobs() {
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
-   this.statusMsg.next(null);
    // remove from the job list any jobs for identities that we don't know about
    let oldjobs = this.joblist.value;
    let oldjobkeys = Object.keys(oldjobs);
@@ -87,18 +116,20 @@ private batchinterface: {[id: string] : BatchInterface};
    for (let identity of this.identities.value) {
      if (this.batchinterface[identity.repr()] === undefined) {
        this.getconfig(new Strudelapp(),identity)
-        .subscribe(resp => this.batchinterface[identity.repr()] = resp);
+        .pipe(catchError(this.handleError))
+        .subscribe(resp => this.batchinterface[identity.repr()] = resp,
+                   error => this.httperror(error));
         continue;
      }
      let paramstr = this.buildParams(null,identity,this.batchinterface[identity.repr()]);
      this.http.get<Job[]>(this.Base+'/stat'+'?'+paramstr,options)
-                      .pipe(catchError(this.handleError('getJobs',[])))
-                      .subscribe(resp => this.updateJoblist(resp,identity));
+                      .pipe(catchError(this.handleError))
+                      .subscribe(resp => this.updateJoblist(resp,identity),
+                                 error => this.httperror(error));
                     }
  }
 
  private startPolling() {
-   this.statusMsg.next(null);
    if (!(this.timerSubscription === null)) {
      this.timerSubscription.unsubscribe()
    }
@@ -116,9 +147,15 @@ private batchinterface: {[id: string] : BatchInterface};
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
    return this.http.get<any>(identity.site.url+'getconfig/'+app.name,options)
-                                        .pipe(catchError(this.handleError('getconfig',[])))
+                                        .pipe(catchError(this.handleError))
 }
 
+ submissionError(message: string) {
+   this.statusMsg.next(message);
+   this.busy.next(false);
+   console.log(message);
+ }
+
  submit(app: Strudelapp, identity: Identity, batchinterface: BatchInterface) {
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
@@ -126,43 +163,65 @@ private batchinterface: {[id: string] : BatchInterface};
    this.busy.next(true);
    let paramstr = this.buildParams(app,identity,batchinterface);
    this.http.post<any>(this.Base+'/submit'+'?'+paramstr,{}, options)
-                                         .pipe(catchError(this.handleError('submit',[])))
-                                         .subscribe(resp => this.busy.next(false));
+                                         .pipe(catchError(this.handleError))
+                                         .subscribe(resp => {
+                                                              this.busy.next(false);
+                                                              this.statusMsg.next(null)
+                                                            },
+                                                    error => this.submissionError(error));
  }
 
  submitted(resp: any ) {
    this.busy.next(false);
-   this.statusMsg.next('Updating job list');
+   // this.statusMsg.next(null);
    this.getJobs();
  }
 
  cancel(job: Job) {
-   console.log("In tes cancel");
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
-   this.statusMsg.next(null);
+   // this.statusMsg.next(null);
    let data = {};
-   console.log(job.identity);
-   console.log(job.identity.repr);
    let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]);
    this.http.delete<any>(this.Base+'/cancel/'+job.jobid+'?'+paramstr, options)
-                      .pipe(catchError(this.handleError('cancel',[])))
+                      .pipe(catchError(this.handleError))
                       .subscribe(resp => this.submitted(resp));
-    console.log('tes.cancel complete');
+ }
+
+ public watchAppwindow(appwindow, dialogRef) {
+   if (appwindow.closed) {
+     dialogRef.close();
+   }
  }
 
  public connect(job: Job) {
-   this.statusMsg.next(null);
+   // this.statusMsg.next(null);
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
    this.busy.next(true);
    let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]);
-   let appwindow = window.open(this.Base+"/connect/"+job.jobid+"/"+job.batch_host+"?"+paramstr);
-   appwindow.focus();
+   // let appwindow = window.open(this.Base+"/connect/"+job.jobid+"/"+job.batch_host+"?"+paramstr);
+
+   // let windowloc = this.Base+"/connect/"+job.jobid+"/"+job.batch_host+"?"+paramstr;
+   let url = this.Base+"/connect/"+job.jobid+"/"+job.batch_host;
+   let windowloc = './connecting?connecturl='+url+"&"+paramstr;
+   // let windowloc = this.router.config
+   this.appwindow = window.open(windowloc);
+   if (!(this.appwindowWatcher === null)) {
+     this.appwindowWatcher.unsubscribe()
+   }
+
+   let dialogRef = this.dialog.open(ModaldialogComponent, {
+     width: '600px',
+   });
+   this.appwindowWatcher = timer(500).pipe(repeat()).subscribe(() => this.watchAppwindow(this.appwindow,dialogRef));
+   // this.appwindow.location.assign(windowloc);
+   this.appwindow.focus();
    this.busy.next(false);
  }
 
-private sshAdd(keyCert: KeyCert) {
+
+public sshAdd(keyCert: KeyCert) {
   if (keyCert.key == undefined) {
     return;
   }
@@ -172,19 +231,19 @@ private sshAdd(keyCert: KeyCert) {
   this.statusMsg.next("Authorising ...")
   let data = {'key': keyCert.key, 'cert': keyCert.cert};
   this.http.post<any>(this.Base+'/sshagent',data,options)
-                      .pipe(catchError(this.handleError('storeCert',[])))
+                      .pipe(catchError(this.handleError))
                       .subscribe(resp => this.getIdentities(),
-                                error => this.httperror(error));
+                                 error => this.httperror(error))
 }
 
-private getIdentities() {
-  console.log('retrieving current identities');
-  this.statusMsg.next("Updating the list of available accounts")
+public getIdentities() {
+  this.statusMsg.next("Updating the list of available accounts");
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
   this.http.get<any>(this.Base+'/sshagent',options)
-                    .pipe(catchError(this.handleError('getIdentities',[])))
-                    .subscribe(resp => this.updateIdentities(resp));
+                    .pipe(catchError(this.handleError))
+                    .subscribe(resp => this.updateIdentities(resp),
+                               error => this.httperror(error))
 }
 
 private killAgent() {
@@ -192,30 +251,39 @@ private killAgent() {
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
   this.http.delete<any>(this.Base+'/sshagent',options)
-                    .pipe(catchError(this.handleError('killAgent',[])))
-                    .subscribe(resp => this.updateIdentities(resp));
+                    .pipe(catchError(this.handleError))
+                    .subscribe(resp => this.updateIdentities(resp),
+                               error => this.httperror(error));
 }
 
 private updateIdentities(resp) {
-  //TODO Each cert as the signing CA parameter. Use this to find the compute sites
-  // rather than just assuming sites[0]
   let certcontents = resp;
   let identities = [];
-  let sites = this.computesite.getComputeSites();
+  var cs: any;
   let idsShort = []
-  for (let i in certcontents) {
-    identities.push(new Identity(certcontents[i].Principals[0],sites[0]))
+  for (cs of this.computesite.returnComputeSites()) {
+    let computesite = <Computesite>cs;
+    for (let i in certcontents) {
+      var fp: string = (<Computesite>cs).cafingerprint;
+      for (let ca of certcontents[i]['Signing CA']) {
+        if (ca == fp) {
+          identities.push(new Identity(certcontents[i].Principals[0],<Computesite>cs))
+        }
+      }
+    }
   }
   this.identities.next(identities);
+  this.statusMsg.next(null);
   if (identities.length == 0) {
-    this.statusMsg.next(null);
     return;
   }
   this.startPolling();
 }
 
 public logout(): Boolean {
-  this.statusMsg.next(null);
+  for (let id of this.identities.value) {
+    console.log('logout identitiy',id.authservice);
+  }
   this.killAgent();
   return true;
 }
@@ -224,25 +292,40 @@ public getstatusMsgSubject(): BehaviorSubject<any> {
   return this.statusMsg;
 }
 
-private httperror(error: any) {
-  console.log(error);
+private httperror(errorstr: string) {
+  var re = /login expired/gi;
+  let searchresult = errorstr.search(re);
+  if (searchresult != -1) {
+    this.statusMsg.next("Some authentication tokens have expired, you may need to log in again");
+    this.getIdentities();
+  }
+  console.log(errorstr);
 }
 
-private handleError<T> (operation = 'operation', result?: T) {
-  return (error: any): Observable<T> => {
-    console.log('in handle error',operation);
-    console.log(error.status);
-
-    if ( operation == 'getJobs') {
-      this.statusMsg.next("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running.");
-    } else if (operation == 'submit') {
-      this.statusMsg.next("Hmm, I couldn't submit that job")
-    } else if (operation == 'getIdentities') {
-      this.statusMsg.next("Hmm, I coudln't get any data from the backend. Try refreshing this page")
-    } else {
-      console.error(error);
+private handleError(error: HttpErrorResponse) {
+  if (error.error instanceof ErrorEvent) {
+    console.log('instance of ErrorEvent');
+    console.log(error);
+    // A client-side or network error occurred. Handle it accordingly.
+    return throwError("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running.");
+  } else {
+    console.log('NOT an instance of ErrorEvent');
+    console.log(error);
+    // Not sure if this code works correctly. It should update identities in case the error is
+    // that the user isn't allowed to run the job
+
+    var re = /identity/gi;
+    let searchresult = error.error.message.search(re);
+    if (searchresult != -1) {
+      // this.getIdentities();
+      return throwError('login expired, refreshing');
     }
-    return of(result as T);
-  };
+    return throwError(error.error.message)
+    // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message);
+
+  }
 }
+
+
+
 }
diff --git a/src/app/teserrors/teserrors.component.html b/src/app/teserrors/teserrors.component.html
index d8a160b2f58aa03e7b8ec2c46d9307eaa441d255..78998789efa824997dd15d89d8fd28738f22a497 100644
--- a/src/app/teserrors/teserrors.component.html
+++ b/src/app/teserrors/teserrors.component.html
@@ -1,3 +1,3 @@
-<p>
+<!-- <p>
 {{ statusMsg }}
-</p>
+</p> -->
diff --git a/src/app/teserrors/teserrors.component.ts b/src/app/teserrors/teserrors.component.ts
index 607b7d1c2e728cf7f4d1319de487611eddcc060f..386fc599033ed3ef640e838dda4297df2819f1fb 100644
--- a/src/app/teserrors/teserrors.component.ts
+++ b/src/app/teserrors/teserrors.component.ts
@@ -1,5 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { TesService } from '../tes.service';
+import { MatSnackBar } from '@angular/material';
+
 
 @Component({
   selector: 'app-teserrors',
@@ -8,22 +10,22 @@ import { TesService } from '../tes.service';
 })
 export class TeserrorsComponent implements OnInit {
 
-  public statusMsg: string;
-
-  constructor(private tesService: TesService,) { }
-
+  // public statusMsg: string;
+  private snackBarRef: any;
+  constructor(private tesService: TesService, public snackBar: MatSnackBar,) { }
   ngOnInit() {
-    console.log('constructing message component');
-    this.tesService.getstatusMsgSubject().subscribe( msg => this.getNewMessage(msg));
-    console.log('constructing message component complete');
-
+    // this.tesService.getstatusMsgSubject().subscribe( msg => this.getNewMessage(msg));
   }
 
   getNewMessage(msg) {
-    console.log('teserrors get message');
-    this.statusMsg = msg;
-    console.log('teserrors get message complete');
-
+    if (msg === null) {
+      console.log('attempting to dismis snackbar');
+      if (this.snackBarRef != undefined) {
+        this.snackBarRef.dismiss()
+      }
+    } else {
+      this.snackBarRef = this.snackBar.open(msg,'Dismiss');
+    }
   }
 
 }
diff --git a/src/styles.css b/src/styles.css
index 5f69d4f7f3fd9c31aaf694a3b57fb4007c22918c..f81c956193d096ce740bbff4ecaee6307770d88a 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1,2 +1,3 @@
 /* You can add global styles to this file, and also import other style files */
+@import url( 'https://fonts.googleapis.com/css?family=Roboto:400,700|Material+Icons');
 @import "~@angular/material/prebuilt-themes/indigo-pink.css";