diff --git a/src/api/dto/course.ts b/src/api/dto/course.ts
new file mode 100644
index 0000000..8b13f1f
--- /dev/null
+++ b/src/api/dto/course.ts
@@ -0,0 +1,23 @@
+import { Course, Identifiable } from "@/data";
+import { Transformer } from "@/serialization";
+
+export interface CourseDTO extends Identifiable {
+    name: string;
+}
+
+export const courseDtoTransformer: Transformer<CourseDTO, Course> = {
+    reverseTransform(subject: Course, context: undefined): CourseDTO {
+        return {
+            id: subject.id,
+            name: subject.name,
+        };
+    },
+    transform(subject: CourseDTO, context: undefined): Course {
+        return {
+            id: subject.id,
+            name: subject.name,
+            desiredSemesters: [],
+            possibleProgramEntries: [], // todo
+        };
+    }
+}
diff --git a/src/api/dto/edition.ts b/src/api/dto/edition.ts
new file mode 100644
index 0000000..94c3101
--- /dev/null
+++ b/src/api/dto/edition.ts
@@ -0,0 +1,57 @@
+import { Identifiable } from "@/data";
+import { CourseDTO, courseDtoTransformer } from "@/api/dto/course";
+import { OneWayTransformer, Transformer } from "@/serialization";
+import { Edition } from "@/data/edition";
+import moment from "moment";
+import { Subset } from "@/helpers";
+
+export interface EditionDTO extends Identifiable {
+    editionStart: string,
+    editionFinish: string,
+    reportingStart: string,
+    course: CourseDTO,
+}
+
+export interface EditionTeaserDTO extends Identifiable {
+    editionStart: string,
+    editionFinish: string,
+    courseName: string,
+}
+
+export const editionTeaserDtoTransformer: OneWayTransformer<EditionTeaserDTO, Subset<Edition>> = {
+    transform(subject: EditionTeaserDTO, context?: undefined): Subset<Edition> {
+        return {
+            id: subject.id,
+            startDate: moment(subject.editionStart),
+            endDate: moment(subject.editionFinish),
+            course: {
+                name: subject.courseName,
+            }
+        }
+    }
+}
+
+export const editionDtoTransformer: Transformer<EditionDTO, Edition> = {
+    reverseTransform(subject: Edition, context: undefined): EditionDTO {
+        return {
+            id: subject.id,
+            editionFinish: subject.endDate.toISOString(),
+            editionStart: subject.startDate.toISOString(),
+            course: courseDtoTransformer.reverseTransform(subject.course),
+            reportingStart: subject.reportingStart.toISOString(),
+        };
+    },
+    transform(subject: EditionDTO, context: undefined): Edition {
+        return {
+            id: subject.id,
+            course: courseDtoTransformer.transform(subject.course),
+            startDate: moment(subject.editionStart),
+            endDate: moment(subject.editionFinish),
+            minimumInternshipHours: 40,
+            maximumInternshipHours: 160,
+            proposalDeadline: moment(subject.reportingStart),
+            reportingStart: moment(subject.reportingStart),
+            reportingEnd: moment(subject.reportingStart).add(1, 'month'),
+        };
+    }
+}
diff --git a/src/api/dto/page.ts b/src/api/dto/page.ts
new file mode 100644
index 0000000..390fa03
--- /dev/null
+++ b/src/api/dto/page.ts
@@ -0,0 +1,40 @@
+import { Identifiable } from "@/data";
+import { Page } from "@/data/page";
+import { Transformer } from "@/serialization";
+
+export interface PageDTO extends Identifiable {
+    accessName: string;
+    title: string;
+    titleEng: string;
+    content: string;
+    contentEng: string;
+}
+
+export const pageDtoTransformer: Transformer<PageDTO, Page> = {
+    reverseTransform(subject: Page, context: undefined): PageDTO {
+        return {
+            id: subject.id,
+            accessName: subject.slug,
+            content: subject.content.pl,
+            contentEng: subject.content.en,
+            title: subject.title.pl,
+            titleEng: subject.title.en,
+        }
+    },
+    transform(subject: PageDTO, context: undefined): Page {
+        return {
+            slug: subject.accessName,
+            id: subject.id,
+            content: {
+                pl: subject.content,
+                en: subject.contentEng
+            },
+            title: {
+                pl: subject.title,
+                en: subject.titleEng
+            },
+        };
+    }
+}
+
+export default pageDtoTransformer;
diff --git a/src/api/dto/student.ts b/src/api/dto/student.ts
new file mode 100644
index 0000000..269e1aa
--- /dev/null
+++ b/src/api/dto/student.ts
@@ -0,0 +1,34 @@
+import { Identifiable, Student } from "@/data";
+import { Transformer } from "@/serialization";
+
+export interface StudentDTO extends Identifiable {
+    albumNumber: number,
+    course: any,
+    email: string,
+    firstName: string,
+    lastName: string,
+    semester: number,
+}
+
+export const studentDtoTransfer: Transformer<StudentDTO, Student> = {
+    reverseTransform(subject: Student, context: undefined): StudentDTO {
+        return {
+            albumNumber: subject.albumNumber,
+            course: subject.course,
+            email: subject.email,
+            firstName: subject.name,
+            lastName: subject.surname,
+            semester: subject.semester
+        };
+    },
+    transform(subject: StudentDTO, context: undefined): Student {
+        return {
+            albumNumber: subject.albumNumber,
+            course: subject.course,
+            email: subject.email,
+            name: subject.firstName,
+            semester: subject.semester,
+            surname: subject.lastName
+        };
+    }
+}
diff --git a/src/api/edition.ts b/src/api/edition.ts
index bce8982..6164634 100644
--- a/src/api/edition.ts
+++ b/src/api/edition.ts
@@ -1,16 +1,16 @@
 import { axios } from "@/api/index";
 import { Edition } from "@/data/edition";
-import { sampleEdition } from "@/provider/dummy";
-import { delay } from "@/helpers";
+import { prepare } from "@/routing";
+import { EditionDTO, editionDtoTransformer, editionTeaserDtoTransformer } from "@/api/dto/edition";
 
 const EDITIONS_ENDPOINT = "/editions";
 const EDITION_INFO_ENDPOINT = "/editions/:key";
 const REGISTER_ENDPOINT = "/register";
 
-export async function editions() {
+export async function available() {
     const response = await axios.get(EDITIONS_ENDPOINT);
 
-    return response.data;
+    return (response.data || []).map(editionTeaserDtoTransformer.transform);
 }
 
 export async function join(key: string): Promise<boolean> {
@@ -23,13 +23,9 @@ export async function join(key: string): Promise<boolean> {
     }
 }
 
-// MOCK
 export async function get(key: string): Promise<Edition | null> {
-    await delay(Math.random() * 200 + 100);
+    const response = await axios.get<EditionDTO>(prepare(EDITION_INFO_ENDPOINT, { key }));
+    const dto = response.data;
 
-    if (key == "inf2020") {
-        return sampleEdition;
-    }
-
-    return null;
+    return editionDtoTransformer.transform(dto);
 }
diff --git a/src/api/index.ts b/src/api/index.ts
index 6d9942d..3432330 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -6,6 +6,7 @@ import { UserState } from "@/state/reducer/user";
 import * as user from "./user";
 import * as edition from "./edition";
 import * as page from "./page"
+import * as student from "./student"
 
 export const axios = Axios.create({
     baseURL: process.env.API_BASE_URL || "https://system-praktyk.stg.kadet.net/api/",
@@ -31,7 +32,8 @@ axios.interceptors.request.use(config => {
 const api = {
     user,
     edition,
-    page
+    page,
+    student
 }
 
 export default api;
diff --git a/src/api/page.tsx b/src/api/page.tsx
index f1c69ee..1a5cd02 100644
--- a/src/api/page.tsx
+++ b/src/api/page.tsx
@@ -1,27 +1,13 @@
-// MOCK
 import { Page } from "@/data/page";
+import { PageDTO, pageDtoTransformer } from "./dto/page"
+import { axios } from "@/api/index";
+import { prepare } from "@/routing";
 
-const tos = `<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Bestiarum vero nullum iudicium puto. Quare ad ea primum, si videtur; <b>Duo Reges: constructio interrete.</b> <i>Eam tum adesse, cum dolor omnis absit;</i> Sed ad bona praeterita redeamus. <mark>Facillimum id quidem est, inquam.</mark> Apud ceteros autem philosophos, qui quaesivit aliquid, tacet; </p>
-
-<p><a href="http://loripsum.net/" target="_blank">Quorum altera prosunt, nocent altera.</a> Eam stabilem appellas. <i>Sed nimis multa.</i> Quo plebiscito decreta a senatu est consuli quaestio Cn. Sin laboramus, quis est, qui alienae modum statuat industriae? <mark>Quod quidem nobis non saepe contingit.</mark> Si autem id non concedatur, non continuo vita beata tollitur. <a href="http://loripsum.net/" target="_blank">Illum mallem levares, quo optimum atque humanissimum virum, Cn.</a> <i>Id est enim, de quo quaerimus.</i> </p>
-
-<p>Ille vero, si insipiens-quo certe, quoniam tyrannus -, numquam beatus; Sin dicit obscurari quaedam nec apparere, quia valde parva sint, nos quoque concedimus; Et quod est munus, quod opus sapientiae? Ab hoc autem quaedam non melius quam veteres, quaedam omnino relicta. </p>
-`
+const STATIC_PAGE_ENDPOINT = "/staticPage/:slug"
 
 export async function get(slug: string): Promise<Page> {
-    if (slug === "/regulamin" || slug === "/rules") {
-        return {
-            id: "tak",
-            content: {
-                pl: tos,
-                en: tos,
-            },
-            title: {
-                pl: "Regulamin Praktyk",
-                en: "Terms of Internship",
-            },
-        }
-    }
+    const response = await axios.get<PageDTO>(prepare(STATIC_PAGE_ENDPOINT, { slug }))
+    const page = response.data;
 
-    throw new Error();
+    return pageDtoTransformer.transform(page);
 }
diff --git a/src/api/student.ts b/src/api/student.ts
new file mode 100644
index 0000000..e93a861
--- /dev/null
+++ b/src/api/student.ts
@@ -0,0 +1,13 @@
+import { axios } from "@/api/index";
+import { Student } from "@/data/student";
+import { StudentDTO, studentDtoTransfer } from "@/api/dto/student";
+
+export const CURRENT_STUDENT_ENDPOINT = '/students/current';
+
+export async function current(): Promise<Student> {
+    const response = await axios.get<StudentDTO>(CURRENT_STUDENT_ENDPOINT);
+    const dto = response.data;
+
+    return studentDtoTransfer.transform(dto);
+}
+
diff --git a/src/api/user.ts b/src/api/user.ts
index ba70d5f..e930190 100644
--- a/src/api/user.ts
+++ b/src/api/user.ts
@@ -1,9 +1,22 @@
 import { axios } from "@/api/index";
+import { query, route } from "@/routing";
 
-const AUTHORIZE_ENDPOINT = "/access/login"
+const LOGIN_ENDPOINT = "/access/login"
 
-export async function authorize(code: string): Promise<string> {
-    const response = await axios.get<string>(AUTHORIZE_ENDPOINT, { params: { code }});
+const CLIENT_ID = process.env.LOGIN_CLIENT_ID || "PraktykiClientId";
+const AUTHORIZE_URL = process.env.AUTHORIZE || "https://logowanie.pg.edu.pl/oauth2.0/authorize";
+
+export async function login(code: string): Promise<string> {
+    const response = await axios.get<string>(LOGIN_ENDPOINT, { params: { code }});
 
     return response.data;
 }
+
+export function getAuthorizeUrl() {
+    return query(AUTHORIZE_URL, {
+        response_type: "code",
+        scope: "user_details",
+        client_id: CLIENT_ID,
+        redirect_uri: window.location.origin + route("user_login") + "/check/pg",
+    })
+}
diff --git a/src/app.tsx b/src/app.tsx
index c05b38c..a3bb940 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,16 +1,14 @@
 import React, { HTMLProps, useEffect } from 'react';
 import { Link, Route, Switch } from "react-router-dom"
-import { route, routes } from "@/routing";
+import { processMiddlewares, route, routes } from "@/routing";
 import { useSelector } from "react-redux";
-import { AppState, isReady } from "@/state/reducer";
+import { AppState } from "@/state/reducer";
 import { Trans, useTranslation } from "react-i18next";
 import { Student } from "@/data";
 import '@/styles/overrides.scss'
 import '@/styles/header.scss'
 import '@/styles/footer.scss'
 import classNames from "classnames";
-import { EditionActions } from "@/state/actions/edition";
-import { sampleEdition } from "@/provider/dummy/edition";
 import { Edition } from "@/data/edition";
 import { SettingActions } from "@/state/actions/settings";
 import { useDispatch, UserActions } from "@/state/actions";
@@ -68,20 +66,12 @@ function App() {
     const { t } = useTranslation();
     const locale = useSelector<AppState, Locale>(state => getLocale(state.settings));
 
-    useEffect(() => {
-        if (!edition) {
-            dispatch({ type: EditionActions.Set, edition: sampleEdition });
-        }
-    })
-
     useEffect(() => {
         i18n.changeLanguage(locale);
         document.documentElement.lang = locale;
         moment.locale(locale)
     }, [ locale ])
 
-    const ready = useSelector(isReady);
-
     return <>
         <header className="header">
             <div id="logo" className="header__logo">
@@ -106,7 +96,11 @@ function App() {
             </div>
         </header>
         <main id="content">
-            { ready && <Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch> }
+            { <Switch>
+                { routes.map(({ name, content, middlewares = [], ...route }) => <Route { ...route } key={ name }>
+                    { processMiddlewares([ ...middlewares, content ]) }
+                </Route>) }
+            </Switch> }
         </main>
         <footer className="footer">
             <Container>
diff --git a/src/components/section.tsx b/src/components/section.tsx
index c91449d..f2a4189 100644
--- a/src/components/section.tsx
+++ b/src/components/section.tsx
@@ -20,3 +20,5 @@ export const Section = ({ children, ...props }: PaperProps) => {
 export const Label = ({ children }: TypographyProps) => {
     return <Typography variant="subtitle2" className="proposal__header">{ children }</Typography>
 }
+
+Section.Label = Label;
diff --git a/src/data/edition.ts b/src/data/edition.ts
index 8abaa77..6ca8725 100644
--- a/src/data/edition.ts
+++ b/src/data/edition.ts
@@ -1,14 +1,17 @@
 import { Moment } from "moment";
 import { Course } from "@/data/course";
+import { Identifiable } from "@/data/common";
 
 export type Edition = {
     course: Course;
     startDate: Moment;
     endDate: Moment;
     proposalDeadline: Moment;
+    reportingStart: Moment,
+    reportingEnd: Moment,
     minimumInternshipHours: number;
     maximumInternshipHours?: number;
-}
+} & Identifiable
 
 export type Deadlines = {
     personalData?: Moment;
diff --git a/src/data/page.ts b/src/data/page.ts
index a474e82..ca7e3c9 100644
--- a/src/data/page.ts
+++ b/src/data/page.ts
@@ -3,4 +3,5 @@ import { Identifiable, Multilingual } from "@/data/common";
 export interface Page extends Identifiable {
     title: Multilingual<string>;
     content: Multilingual<string>;
+    slug: string;
 }
diff --git a/src/data/student.ts b/src/data/student.ts
index b8eabfc..d649fbd 100644
--- a/src/data/student.ts
+++ b/src/data/student.ts
@@ -7,7 +7,7 @@ export interface Student extends Identifiable {
     name: string;
     surname: string;
     email: string;
-    albumNumber: string;
+    albumNumber: number;
     semester: Semester;
     course: Course;
 }
diff --git a/src/helpers.ts b/src/helpers.ts
index ae9da92..fc5e38e 100644
--- a/src/helpers.ts
+++ b/src/helpers.ts
@@ -1,6 +1,6 @@
 export type Nullable<T> = { [P in keyof T]: T[P] | null }
 
-export type Partial<T> = { [K in keyof T]?: T[K] }
+export type Subset<T> = { [K in keyof T]?: Subset<T[K]> }
 export type Dictionary<T> = { [key: string]: T };
 
 export type Index = string | symbol | number;
diff --git a/src/hooks/useAsync.ts b/src/hooks/useAsync.ts
index 91e1de2..a8293d4 100644
--- a/src/hooks/useAsync.ts
+++ b/src/hooks/useAsync.ts
@@ -8,12 +8,14 @@ export type AsyncResult<T, TError = any> = {
 
 export type AsyncState<T, TError = any> = [AsyncResult<T, TError>, (promise: Promise<T> | undefined) => void]
 
-export function useAsync<T, TError = any>(promise: Promise<T> | undefined): AsyncResult<T, TError> {
+export function useAsync<T, TError = any>(supplier: Promise<T> | (() => Promise<T>) | undefined): AsyncResult<T, TError> {
     const [isLoading, setLoading] = useState<boolean>(true);
     const [error, setError] = useState<TError | undefined>(undefined);
     const [value, setValue] = useState<T | undefined>(undefined);
     const [semaphore] = useState<{ value: number }>({ value: 0 })
 
+    const [promise, setPromise] = useState(typeof supplier === "function" ? null : supplier)
+
     useEffect(() => {
         setLoading(true);
         setError(undefined);
@@ -35,6 +37,12 @@ export function useAsync<T, TError = any>(promise: Promise<T> | undefined): Asyn
         })
     }, [ promise ])
 
+    useEffect(() => {
+        if (typeof supplier === "function") {
+            setPromise(supplier());
+        }
+    }, [])
+
     return {
         isLoading,
         value,
diff --git a/src/middleware.tsx b/src/middleware.tsx
new file mode 100644
index 0000000..7d16ca3
--- /dev/null
+++ b/src/middleware.tsx
@@ -0,0 +1,15 @@
+import { Middleware, route } from "@/routing";
+import { useSelector } from "react-redux";
+import { isReady } from "@/state/reducer";
+import { Redirect } from "react-router-dom";
+import React from "react";
+
+export const isReadyMiddleware: Middleware<any, any> = next => {
+    const ready = useSelector(isReady);
+
+    if (ready) {
+        return next();
+    }
+
+    return <Redirect to={ route("edition_pick") } />;
+}
diff --git a/src/pages/edition/pick.tsx b/src/pages/edition/pick.tsx
new file mode 100644
index 0000000..5275408
--- /dev/null
+++ b/src/pages/edition/pick.tsx
@@ -0,0 +1,72 @@
+import { Page } from "@/pages/base";
+import React from "react";
+import { useTranslation } from "react-i18next";
+import { Box, Button, CircularProgress, Container, Typography } from "@material-ui/core";
+import { Actions } from "@/components";
+import { Link as RouterLink, useHistory } from "react-router-dom"
+import { route } from "@/routing";
+import { AccountArrowRight } from "mdi-material-ui";
+import { useAsync } from "@/hooks";
+import api from "@/api";
+import { Section } from "@/components/section";
+import { useVerticalSpacing } from "@/styles";
+import { Alert } from "@material-ui/lab";
+import { EditionActions, useDispatch } from "@/state/actions";
+
+export const PickEditionPage = () => {
+    const { t } = useTranslation();
+    const { value: editions, isLoading } = useAsync(() => api.edition.available());
+
+    const dispatch = useDispatch();
+    const history = useHistory();
+
+    const classes = useVerticalSpacing(3);
+
+    const pickEditionHandler = (id: string) => async () => {
+        const edition = await api.edition.get(id);
+
+        if (!edition) {
+            return;
+        }
+
+        dispatch({
+            type: EditionActions.Set,
+            edition
+        })
+
+        history.push("/");
+    }
+
+    return <Page>
+        <Page.Header maxWidth="md">
+            <Page.Title>{ t("pages.pick-edition.title") }</Page.Title>
+        </Page.Header>
+        <Container className={ classes.root }>
+            <Typography variant="h3">{ t("pages.pick-edition.my-editions") }</Typography>
+            { isLoading ? <CircularProgress /> : <div>
+                { editions.length > 0 ? editions.map((edition: any) =>
+                    <Section key={ edition.id }>
+                        <Typography className="proposal__primary">{ edition.course.name }</Typography>
+                        <Typography className="proposal__secondary">
+                            { t('internship.date-range', { start: edition.startDate, end: edition.endDate }) }
+                        </Typography>
+                        <Box mt={2}>
+                            <Actions>
+                                <Button variant="contained" color="primary" onClick={ pickEditionHandler(edition.id) }>{ t('pages.pick-edition.pick') }</Button>
+                            </Actions>
+                        </Box>
+                    </Section>
+                ) : <Alert severity="info">{ t("pages.pick-edition.no-editions") }</Alert> }
+            </div> }
+            <Actions>
+                <Button variant="contained" color="primary"
+                        startIcon={ <AccountArrowRight /> }
+                        component={ RouterLink } to={ route("edition_register") }>
+                    { t("pages.pick-edition.register") }
+                </Button>
+            </Actions>
+        </Container>
+    </Page>
+}
+
+export default PickEditionPage;
diff --git a/src/pages/main.tsx b/src/pages/main.tsx
index 73d7cab..28215eb 100644
--- a/src/pages/main.tsx
+++ b/src/pages/main.tsx
@@ -25,7 +25,7 @@ export const MainPage = () => {
 
     const missingStudentData = useMemo(() => student ? getMissingStudentData(student) : [], [student]);
 
-    useEffect(() => void api.edition.editions())
+    useEffect(() => void api.edition.available())
 
     if (!student) {
         return <Redirect to={ route("user_login") }/>;
diff --git a/src/pages/user/login.tsx b/src/pages/user/login.tsx
index 89fa966..46a5a00 100644
--- a/src/pages/user/login.tsx
+++ b/src/pages/user/login.tsx
@@ -1,46 +1,78 @@
-import React, { Dispatch } from "react";
+import React, { Dispatch, useEffect } from "react";
 import { Page } from "@/pages/base";
-import { Button, Container, Typography } from "@material-ui/core";
-import { Action, useDispatch } from "@/state/actions";
-import { useHistory } from "react-router-dom";
+import { Button, Container } from "@material-ui/core";
+import { Action, StudentActions, useDispatch } from "@/state/actions";
+import { Route, Switch, useHistory, useLocation, useRouteMatch } from "react-router-dom";
 import { route } from "@/routing";
 import { useVerticalSpacing } from "@/styles";
 import { AppState } from "@/state/reducer";
 
 import api from "@/api";
 import { UserActions } from "@/state/actions/user";
-import { sampleStudent } from "@/provider/dummy";
+import { getAuthorizeUrl } from "@/api/user";
 
-const authorizeUser = async (dispatch: Dispatch<Action>, getState: () => AppState): Promise<void> => {
-    const token = await api.user.authorize("test");
+const authorizeUser = (code: string) => async (dispatch: Dispatch<Action>, getState: () => AppState): Promise<void> => {
+    const token = await api.user.login(code);
 
     dispatch({
         type: UserActions.Login,
         token,
-        student: sampleStudent,
+    })
+
+    const student = await api.student.current();
+    dispatch({
+        type: StudentActions.Set,
+        student: student,
     })
 }
 
 export const UserLoginPage = () => {
     const dispatch = useDispatch();
     const history  = useHistory();
+    const match    = useRouteMatch();
+    const location = useLocation();
+    const query    = new URLSearchParams(useLocation().search);
 
     const handleSampleLogin = async () => {
-        await dispatch(authorizeUser);
+        await dispatch(authorizeUser("test"));
 
         history.push(route("home"));
     }
 
+    const handlePgLogin = async () => {
+        history.push(route("user_login") + "/pg");
+    }
+
     const classes = useVerticalSpacing(3);
 
+    useEffect(() => {
+        (async function() {
+            if (location.pathname === `${match.path}/check/pg`) {
+                await dispatch(authorizeUser(query.get("code") as string));
+                history.push("/");
+            }
+        })();
+    }, [ match.path ]);
+
     return <Page>
         <Page.Header maxWidth="md">
-            <Page.Title>Tu miało być przekierowanie do logowania PG...</Page.Title>
+            <Page.Title>Zaloguj się</Page.Title>
         </Page.Header>
-        <Container maxWidth="md" className={ classes.root }>
-            <Typography variant="h3">... ale wciąż czekamy na dostęp :(</Typography>
-
-            <Button fullWidth onClick={ handleSampleLogin } variant="contained" color="primary">Zaloguj jako przykładowy student</Button>
+        <Container>
+            <Switch>
+                <Route path={match.path} exact>
+                    <Container maxWidth="md" className={ classes.root }>
+                        <Button fullWidth onClick={ handlePgLogin } variant="contained" color="primary">Zaloguj się z pomocą konta PG</Button>
+                        <Button fullWidth onClick={ handleSampleLogin } variant="contained" color="secondary">Zaloguj jako przykładowy student</Button>
+                    </Container>
+                </Route>
+                <Route path={`${match.path}/pg`} render={
+                    () => (window.location.href = getAuthorizeUrl())
+                } />
+                <Route path={`${match.path}/check/pg`}>
+                    Kod: { query.get("code") }
+                </Route>
+            </Switch>
         </Container>
     </Page>;
 }
diff --git a/src/provider/dummy/student.ts b/src/provider/dummy/student.ts
index 1e2387c..38272d7 100644
--- a/src/provider/dummy/student.ts
+++ b/src/provider/dummy/student.ts
@@ -32,7 +32,7 @@ export const sampleStudent: Student = {
     id: studentIdSequence(),
     name: "Jan",
     surname: "Kowalski",
-    albumNumber: "123456",
+    albumNumber: 123456,
     email: "s123456@student.pg.edu.pl",
     course: sampleCourse,
     semester: 6,
diff --git a/src/routing.tsx b/src/routing.tsx
index f57249d..f9ef81f 100644
--- a/src/routing.tsx
+++ b/src/routing.tsx
@@ -6,26 +6,43 @@ import { FallbackPage } from "@/pages/fallback";
 import SubmitPlanPage from "@/pages/internship/plan";
 import { UserLoginPage } from "@/pages/user/login";
 import { RegisterEditionPage } from "@/pages/edition/register";
+import PickEditionPage from "@/pages/edition/pick";
+import { isReadyMiddleware } from "@/middleware";
 
 type Route = {
     name?: string;
     content: () => ReactComponentElement<any>,
     condition?: () => boolean,
+    middlewares?: Middleware<any, any>[],
 } & RouteProps;
 
+export type Middleware<TReturn, TArgs extends any[]> = (next: () => any, ...args: TArgs) => TReturn;
+
+export function processMiddlewares<TArgs extends any[]>(middleware: Middleware<any, TArgs>[], ...args: TArgs): any {
+    if (middleware.length == 0) {
+        return null;
+    }
+
+    const current = middleware.slice(0, 1)[0];
+    const left    = middleware.slice(1);
+
+    return current(() => processMiddlewares(left, ...args), ...args);
+}
+
 export const routes: Route[] = [
-    { name: "home", path: "/", exact: true, content: () => <MainPage/> },
+    { name: "home", path: "/", exact: true, content: () => <MainPage/>, middlewares: [ isReadyMiddleware ] },
 
     // edition
     { name: "edition_register", path: "/edition/register", exact: true, content: () => <RegisterEditionPage/> },
+    { name: "edition_pick", path: "/edition/pick", exact: true, content: () => <PickEditionPage/> },
 
     // internship
-    { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => <InternshipProposalFormPage/> },
-    { name: "internship_proposal_preview", path: "/internship/preview/proposal", exact: true, content: () => <InternshipProposalPreviewPage/> },
+    { name: "internship_proposal", path: "/internship/proposal", exact: true, content: () => <InternshipProposalFormPage/>, middlewares: [ isReadyMiddleware ] },
+    { name: "internship_proposal_preview", path: "/internship/preview/proposal", exact: true, content: () => <InternshipProposalPreviewPage/>, middlewares: [ isReadyMiddleware ] },
     { name: "internship_plan", path: "/internship/plan", exact: true, content: () => <SubmitPlanPage/> },
 
     // user
-    { name: "user_login", path: "/user/login", exact: true, content: () => <UserLoginPage /> },
+    { name: "user_login", path: "/user/login", content: () => <UserLoginPage/> },
 
     // fallback route for 404 pages
     { name: "fallback", path: "*", content: () => <FallbackPage/> }
@@ -44,3 +61,9 @@ export function route(name: string, params: URLParams = {}) {
 
     return prepare(url, params)
 }
+
+export const query = (url: string, params: URLParams) => {
+    const query = Object.entries(params).map(([name, value]) => `${ name }=${ encodeURIComponent(value) }`).join("&");
+
+    return url + (query.length > 0 ? `?${ query }` : '');
+}
diff --git a/src/serialization/types.ts b/src/serialization/types.ts
index b7528c6..befd035 100644
--- a/src/serialization/types.ts
+++ b/src/serialization/types.ts
@@ -11,8 +11,11 @@ type Simplify<T> = string |
 
 export type Serializable<T> = { [K in keyof T]: Simplify<T[K]> }
 export type Transformer<TFrom, TResult, TContext = never> = {
-    transform(subject: TFrom, context?: TContext): TResult;
     reverseTransform(subject: TResult, context?: TContext): TFrom;
-}
+} & OneWayTransformer<TFrom, TResult, TContext>
 
 export type SerializationTransformer<T, TSerialized = Serializable<T>> = Transformer<T, TSerialized>
+
+export type OneWayTransformer<TFrom, TResult, TContext = never> = {
+    transform(subject: TFrom, context?: TContext): TResult;
+}
diff --git a/src/state/actions/edition.ts b/src/state/actions/edition.ts
index 142220d..f25b853 100644
--- a/src/state/actions/edition.ts
+++ b/src/state/actions/edition.ts
@@ -2,7 +2,7 @@ import { Action } from "@/state/actions/base";
 import { Edition } from "@/data/edition";
 
 export enum EditionActions {
-    Set = 'SET',
+    Set = 'SET_EDITION',
 }
 
 export interface SetAction extends Action<EditionActions.Set> {
diff --git a/src/state/actions/index.ts b/src/state/actions/index.ts
index a629b04..043b99b 100644
--- a/src/state/actions/index.ts
+++ b/src/state/actions/index.ts
@@ -8,6 +8,7 @@ import { InsuranceAction, InsuranceActions } from "@/state/actions/insurance";
 import { UserAction, UserActions } from "@/state/actions/user";
 import { ThunkDispatch } from "redux-thunk";
 import { AppState } from "@/state/reducer";
+import { StudentAction, StudentActions } from "@/state/actions/student";
 
 export * from "./base"
 export * from "./edition"
@@ -15,10 +16,26 @@ export * from "./settings"
 export * from "./proposal"
 export * from "./plan"
 export * from "./user"
+export * from "./student"
 
-export type Action = UserAction | EditionAction | SettingsAction | InternshipProposalAction | InternshipPlanAction | InsuranceAction;
+export type Action
+    = UserAction
+    | EditionAction
+    | SettingsAction
+    | InternshipProposalAction
+    | StudentAction
+    | InternshipPlanAction
+    | InsuranceAction;
 
-export const Actions = { ...UserActions, ...EditionActions, ...SettingActions, ...InternshipProposalActions, ...InternshipPlanActions, ...InsuranceActions }
+export const Actions = {
+    ...UserActions,
+    ...EditionActions,
+    ...SettingActions,
+    ...InternshipProposalActions,
+    ...InternshipPlanActions,
+    ...InsuranceActions,
+    ...StudentActions,
+}
 export type Actions = typeof Actions;
 
 export const useDispatch = () => useReduxDispatch<ThunkDispatch<AppState, any, Action>>()
diff --git a/src/state/actions/student.ts b/src/state/actions/student.ts
new file mode 100644
index 0000000..ab9c832
--- /dev/null
+++ b/src/state/actions/student.ts
@@ -0,0 +1,13 @@
+import { Action } from "@/state/actions/base";
+import { Student } from "@/data";
+
+export enum StudentActions {
+    Set = 'SET_STUDENT',
+}
+
+export interface SetStudentAction extends Action<StudentActions.Set> {
+    student: Student,
+}
+
+export type StudentAction = SetStudentAction;
+
diff --git a/src/state/actions/user.ts b/src/state/actions/user.ts
index 6ee23f1..655c1cb 100644
--- a/src/state/actions/user.ts
+++ b/src/state/actions/user.ts
@@ -1,5 +1,4 @@
 import { Action } from "@/state/actions/base";
-import { Student } from "@/data";
 
 export enum UserActions {
     Login = 'LOGIN',
@@ -8,7 +7,6 @@ export enum UserActions {
 
 export interface LoginAction extends Action<UserActions.Login> {
     token: string;
-    student: Student;
 }
 
 export type LogoutAction = Action<UserActions.Logout>;
diff --git a/src/state/reducer/student.ts b/src/state/reducer/student.ts
index 1851bac..f00f52d 100644
--- a/src/state/reducer/student.ts
+++ b/src/state/reducer/student.ts
@@ -1,13 +1,14 @@
 import { Student } from "@/data";
 import { UserAction, UserActions } from "@/state/actions/user";
+import { StudentAction, StudentActions } from "@/state/actions/student";
 
 export type StudentState = Student | null;
 
 const initialStudentState: StudentState = null;
 
-const studentReducer = (state: StudentState = initialStudentState, action: UserAction): StudentState => {
+const studentReducer = (state: StudentState = initialStudentState, action: UserAction | StudentAction): StudentState => {
     switch (action.type) {
-        case UserActions.Login:
+        case StudentActions.Set:
             return action.student;
 
         case UserActions.Logout:
diff --git a/translations/pl.yaml b/translations/pl.yaml
index 2efc081..2d7554f 100644
--- a/translations/pl.yaml
+++ b/translations/pl.yaml
@@ -34,6 +34,11 @@ pages:
     header: "Zgłoszenie praktyki"
   edition:
     header: "Zapisz się do edycji"
+  pick-edition:
+    title: "Wybór edycji"
+    my-editions: "Moje praktyki"
+    pick: "wybierz"
+    register: "Zapisz się do edycji praktyk"
 
 forms:
   internship:
diff --git a/webpack.config.js b/webpack.config.js
index c3636bf..6585967 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -51,11 +51,12 @@ const config = {
     ],
     devServer: {
         contentBase: path.resolve("./public/"),
-        port: 3000,
-        host: 'system-praktyk-front.localhost',
+        host: process.env.APP_HOST || 'system-praktyk-front.localhost',
         disableHostCheck: true,
         historyApiFallback: true,
         overlay: true,
+        https: !!process.env.APP_HTTPS || false,
+        port: parseInt(process.env.APP_PORT || "3000"),
         proxy: {
             "/api": {
                 target: "http://system-praktyk-front.localhost:8080/",