feat: 🎸 Logga in med Freja eID+

This commit is contained in:
Kajetan Kazimierczak 2022-04-01 23:38:47 +02:00
parent 948568dd7c
commit d9c62a6d91
10 changed files with 173 additions and 5 deletions

View File

@ -54,6 +54,7 @@ const LoginMethods: Logins = {
BANKID_SAME_DEVICE: 0,
BANKID_ANOTHER_DEVICE: 2,
TEST_USER: 3,
}
export const Login = () => {
@ -86,6 +87,7 @@ export const Login = () => {
{ id: 'thisdevice', title: t('auth.bankid.OpenOnThisDevice') },
{ id: 'otherdevice', title: t('auth.bankid.OpenOnAnotherDevice') },
{ id: 'testuser', title: t('auth.loginAsTestUser') },
{ id: 'freja', title: t('auth.freja.OpenOnThisDevice') },
] as const
const loginHandler = async () => {
@ -120,12 +122,40 @@ export const Login = () => {
}
}
const openFreja = (token:string) => {
try {
const originAppScheme = encodeURIComponent(schema);
const frejaUrl =
Platform.OS === 'ios'
? `${token}&originAppScheme=${originAppScheme}`
: `${token}&originAppScheme=${originAppScheme}`
Linking.openURL(frejaUrl)
} catch (err) {
setError(t('auth.freja.OpenManually'))
}
}
const isUsingPersonalIdNumber =
loginMethodId === 'otherdevice' ||
(loginMethodId === 'thisdevice' && !loginBankIdSameDeviceWithoutId)
const startLogin = async (text: string) => {
if (loginMethodId === 'thisdevice' || loginMethodId === 'otherdevice') {
if(loginMethodId === 'freja'){
showModal(true)
const status = await api.loginFreja();
setCancelLoginRequest(() => () => status.cancel())
openFreja(status.token)
status.on('STARTED', () => console.log('Freja eID app not yet opened'))
status.on('DELIVERED_TO_MOBILE', () => console.log('Freja eID app is open'))
status.on('CANCELLED', () => {
console.log('User pressed cancel in Freja eID')
showModal(false)
})
status.on('APPROVED', () => console.log('Freja eID ok'))
}
else if (loginMethodId === 'thisdevice' || loginMethodId === 'otherdevice') {
showModal(true)
let ssn

View File

@ -7,7 +7,7 @@ export type ChildPersonalNumbers = Record<string, string>
export const settingsState = proxy({
hydrated: false,
settings: {
loginMethodId: 'thisdevice' as 'thisdevice' | 'otherdevice' | 'testuser',
loginMethodId: 'thisdevice' as 'thisdevice' | 'otherdevice' | 'testuser' | 'freja',
usingSystemTheme: true,
theme: 'light',
cachedPersonalIdentityNumber: '',

View File

@ -23,6 +23,11 @@
"OpenOnThisDevice": "Open BankID on this device",
"Waiting": "Waiting for BankID…"
},
"freja": {
"OpenManually": "Open Freja eID manually",
"OpenOnThisDevice": "Freja eID",
"Waiting": "Waiting for Freja eID…"
},
"chooseLoginMethod": "Choose login method",
"chooseSchoolPlatform": "Choose platform",
"loginAsTestUser": "Log in as a test user",

View File

@ -23,6 +23,11 @@
"OpenOnThisDevice": "Logga in med BankID",
"Waiting": "Väntar på BankID…"
},
"freja": {
"OpenManually": "Öppna BankID manuellt",
"OpenOnThisDevice": "Logga in med Freja eID",
"Waiting": "Väntar på Freja eID…"
},
"chooseLoginMethod": "Välj inloggningsmetod",
"chooseSchoolPlatform": "Välj plattform",
"loginAsTestUser": "Logga in som testanvändare",

View File

@ -8,6 +8,7 @@ import {
Fetch,
Fetcher,
FetcherOptions,
FrejaLoginStatusChecker,
LoginStatusChecker,
MenuItem,
NewsItem,
@ -31,6 +32,7 @@ import { DateTime } from 'luxon'
import * as html from 'node-html-parser'
import * as fake from './fakeData'
import { checkStatus, DummyStatusChecker } from './loginStatusChecker'
import { checkStatus as checkFrejaStatus } from './frejaLoginStatusChecker'
import * as parse from './parse/index'
import queueFetcherWrapper from './queueFetcherWrapper'
import * as routes from './routes'
@ -166,6 +168,45 @@ export class ApiSkolplattformen extends EventEmitter implements Api {
return status
}
public async loginFreja(): Promise<FrejaLoginStatusChecker> {
const loginUrl = routes.frejaLogin
const loginResponse = await this.fetch('auth-ticket', loginUrl)
// if (!ticketResponse.ok) {
// throw new Error(
// `Server Error [${ticketResponse.status}] [${ticketResponse.statusText}] [${ticketUrl}]`
// )
// }
const appSwitchUrl: string = await loginResponse.text()
const cleanAppSwitchUrl = this.cleanFrejaAppSwitchUrl(appSwitchUrl)
console.log('getting freja login url: ' + cleanAppSwitchUrl)
const status = checkFrejaStatus(this.fetch, cleanAppSwitchUrl)
status.on('APPROVED', async () => {
//await this.retrieveFrejaSessionCookie()
await this.retrieveXsrfToken()
this.isLoggedIn = true
this.emit('login')
})
// status.on('ERROR', () => {
// this.personalNumber = undefined
// })
return status
}
private cleanFrejaAppSwitchUrl(url: string): string {
const parts = url.split('&')
return parts[0]
}
public async setSessionCookie(sessionCookie: string): Promise<void> {
// Manually set cookie in this call and let the cookieManager
// handle it from here
@ -190,10 +231,33 @@ export class ApiSkolplattformen extends EventEmitter implements Api {
}
private async retrieveSessionCookie(): Promise<void> {
const url = routes.loginCookie
await this.fetch('login-cookie', url)
}
private async retrieveFrejaSessionCookie(): Promise<void> {
const url = routes.frejaLoginCookie
const session = await this.getSession(url, {
redirect: 'manual',
})
//const session = this.getRequestInit()
console.log(JSON.stringify(session))
const response = await this.fetch('freja-login-return-url', url, session)
console.log(response.status)
console.log(response.text())
console.log(JSON.stringify(response))
const response2 = await this.fetch('freja-login-cookie', url, session)
console.log(response2.status)
console.log(response2.text())
}
private async retrieveXsrfToken(): Promise<void> {
const url = routes.hemPage
const session = this.getRequestInit()

View File

@ -0,0 +1,45 @@
import { EventEmitter } from 'events';
import { frejaLoginStatus } from './routes';
import { Fetcher, FrejaLoginStatusChecker } from '@skolplattformen/api';
export class FrejaChecker extends EventEmitter implements FrejaLoginStatusChecker {
public token: string;
private fetcher: Fetcher;
private url: string;
private cancelled = false;
constructor(fetcher: Fetcher, token: string) {
super();
this.fetcher = fetcher
this.token = token
this.url = frejaLoginStatus;
this.check();
}
async check(): Promise<void> {
const response = await this.fetcher('freja-login-status', this.url);
const status = await response.text();
this.emit(status);
if (!this.cancelled &&
status !== 'APPROVED' &&
// status !== 'ERROR!' &&
status !== 'CANCELLED'
){
setTimeout(() => this.check(), 1000);
}
else console.log('APPROVED');
}
async cancel(): Promise<void> {
this.cancelled = true;
}
}
export const checkStatus = (
fetch: Fetcher,
token: string,
): FrejaLoginStatusChecker => new FrejaChecker(fetch, token)

View File

@ -1,3 +1,4 @@
// BankId
export const login = (personalNumber?: string) => {
const baseUrl = 'https://login003.stockholm.se/NECSadcmbid/authenticate/NECSadcmbid?TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt'
const optionalPersonalNumber = personalNumber === undefined ? '' : `&personalNumber=${personalNumber}`
@ -7,9 +8,17 @@ export const login = (personalNumber?: string) => {
export const loginStatus = (order: string) =>
`https://login003.stockholm.se/NECSadcmbid/authenticate/NECSadcmbid?TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt&verifyorder=${order}&_=${Date.now()}`
export const loginCookie =
export const loginCookie =
'https://login003.stockholm.se/NECSadcmbid/authenticate/SiteMinderAuthADC?TYPE=33554433&REALMOID=06-42f40edd-0c5b-4dbc-b714-1be1e907f2de&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=IfNE0iMOtzq2TcxFADHylR6rkmFtwzoxRKh5nRMO9NBqIxHrc38jFyt56FASdxk1&TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2fmbid%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvR2V0Q2hpbGRyZW4%3d'
// Freja
export const frejaLogin = 'https://login003.stockholm.se/NECSadcfreja/authenticate/NECSadcfreja?action=init&return_url=https%3A%2F%2Flogin003.stockholm.se%2FNECSadcfreja%2Fauthenticate%2FNECSadcfreja'
export const frejaLoginStatus = 'https://login003.stockholm.se/NECSadcfreja/authenticate/NECSadcfreja?TYPE=33554433&REALMOID=06-89cf916c-9764-45fa-8690-eaf3fe9282bc&GUID=1&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=IfNE0iMOtzq2TcxFADHylR6rkmFtwzoxRKh5nRMO9NBqIxHrc38jFyt56FASdxk1&TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2ffreja%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvaGVt&action=checkstatus'
export const frejaReturnUrl = 'https://login003.stockholm.se/NECSadcfreja/authenticate/NECSadcfreja?TYPE=33554433&REALMOID=06-89cf916c-9764-45fa-8690-eaf3fe9282bc&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=IfNE0iMOtzq2TcxFADHylR6rkmFtwzoxRKh5nRMO9NBqIxHrc38jFyt56FASdxk1&TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2ffreja%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvT3ZlcnNpa3Q%3d'
export const frejaLoginCookie = 'https://login003.stockholm.se/NECSadcfreja/authenticate/SiteMinderAuthADCFREJA?TYPE=33554433&REALMOID=06-89cf916c-9764-45fa-8690-eaf3fe9282bc&GUID=&SMAUTHREASON=0&METHOD=GET&SMAGENTNAME=IfNE0iMOtzq2TcxFADHylR6rkmFtwzoxRKh5nRMO9NBqIxHrc38jFyt56FASdxk1&TARGET=-SM-HTTPS%3a%2f%2flogin001%2estockholm%2ese%2fNECSadc%2ffreja%2fb64startpage%2ejsp%3fstartpage%3daHR0cHM6Ly9ldGphbnN0LnN0b2NraG9sbS5zZS92YXJkbmFkc2hhdmFyZS9pbmxvZ2dhZDIvT3ZlcnNpa3Q%3d'
const urlLoggedIn = `https://etjanst.stockholm.se/vardnadshavare/inloggad2`
export const children = `${urlLoggedIn}/GetChildren`

View File

@ -1,7 +1,7 @@
import { Language } from '@skolplattformen/curriculum'
import { EventEmitter } from 'events'
import { DateTime } from 'luxon'
import { LoginStatusChecker } from './loginStatus'
import { LoginStatusChecker, FrejaLoginStatusChecker } from './loginStatus'
import {
CalendarItem,
Classmate,
@ -22,6 +22,7 @@ export interface Api extends EventEmitter {
isLoggedIn: boolean
getPersonalNumber(): string | undefined
login(personalNumber?: string): Promise<LoginStatusChecker>
loginFreja(): Promise<FrejaLoginStatusChecker>
setSessionCookie(sessionCookie: string): Promise<void>
getSessionHeaders(url: string): Promise<{ [index: string]: string }>
getUser(): Promise<User>

View File

@ -2,7 +2,7 @@ import wrap from './fetcher'
export { toMarkdown } from './parseHtml'
export * from './types'
export { LoginStatusChecker } from './loginStatus'
export { LoginStatusChecker, FrejaLoginStatusChecker } from './loginStatus'
export { Api } from './api'
export { FetcherOptions, Fetcher } from './fetcher'
export {

View File

@ -15,3 +15,12 @@ export interface LoginStatusChecker {
) => LoginStatusChecker
cancel: () => Promise<void>
}
export interface FrejaLoginStatusChecker {
token: string
on: (
event: 'APPROVED' | 'STARTED' | 'DELIVERED_TO_MOBILE' | 'CANCELLED',
listener: (...args: any[]) => void
) => FrejaLoginStatusChecker
cancel: () => Promise<void>
}