You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As an unauthenticated teacher, I want a login page with a "Sign in with Edupass" button, so that I can start Edupass sign-in from the workspace instead of typing a route by hand.
Background
Teacher Workspace has no sign-in surface. #75 adds the server-side Edupass routes (/auth/edupass to start sign-in, /auth/edupass/callback to complete it) and explicitly leaves the user interface out of scope: a teacher has to open /auth/edupass directly in the browser. This issue supplies that missing entry point.
The page is a static, unauthenticated surface. Its only action is a top-level navigation to /auth/edupass; it performs no OIDC work of its own and holds no client-side auth state.
Sign-in can fail on the server (mismatched correlation state, an unverifiable ID token, or an error response from the provider), and every one of those failures ends on the server side with nowhere to report it. The agreed contract is that the server redirects back to /login?error=oauth2_callback_failed, and this page turns that parameter into a visible message so a failed sign-in does not dead-end on a blank page. This issue only consumes the parameter; emitting it belongs to the server work.
The workspace shell (SidebarProvider + AppSidebar) currently wraps every route in App.tsx. An unauthenticated teacher has no apps to navigate to, so /login renders outside that shell.
Acceptance criteria
Teacher starts Edupass sign-in from the login page
Given an unauthenticated teacher on /login
When the teacher activates the "Sign in with Edupass" button
Then the browser performs a top-level navigation to /auth/edupass, leaving the single-page app rather than handling the click in the client router
Login page renders outside the workspace sidebar shell
Given an unauthenticated teacher
When the teacher opens /login at any viewport width
Then the sign-in content is displayed, and neither the app sidebar nor the mobile sidebar trigger is present on the page
Failed Edupass sign-in is reported on the login page
Given sign-in failed and the teacher was returned to /login?error=oauth2_callback_failed
When the page loads
Then a message stating that sign-in failed is displayed, and the "Sign in with Edupass" button remains available so the teacher can retry
Login page shows no error on a normal visit
Given a teacher opens /login with no error parameter, an empty error parameter, or an error value other than oauth2_callback_failed
When the page loads
Then no error message is displayed, and the page renders exactly as it does on a first visit
Workspace routes are unaffected by the login page
Given the login page exists
When a teacher opens /, /students, /posts, /groups, or an unknown path
Then each route renders as it did before, inside the workspace sidebar shell, with the sidebar present
Error codes other than oauth2_callback_failed. Any other value renders no error.
Gating routes behind authentication. Every existing route stays reachable while unauthenticated, and nothing redirects an unauthenticated teacher to /login.
Redirecting an already-authenticated teacher away from /login. There is no client-visible session state to check yet.
The demo account sign-in shown in the prototype. It is deliberately excluded.
flowchart LR
A["/login"] -->|"Sign in with Edupass"| B["GET /auth/edupass"]
B -.->|"sign-in fails (#75)"| C["/login?error=oauth2_callback_failed"]
C --> A
B -.->|"sign-in succeeds (#75)"| D["/"]
User story
As an unauthenticated teacher, I want a login page with a "Sign in with Edupass" button, so that I can start Edupass sign-in from the workspace instead of typing a route by hand.
Background
Teacher Workspace has no sign-in surface. #75 adds the server-side Edupass routes (
/auth/edupassto start sign-in,/auth/edupass/callbackto complete it) and explicitly leaves the user interface out of scope: a teacher has to open/auth/edupassdirectly in the browser. This issue supplies that missing entry point.The page is a static, unauthenticated surface. Its only action is a top-level navigation to
/auth/edupass; it performs no OIDC work of its own and holds no client-side auth state.Sign-in can fail on the server (mismatched correlation state, an unverifiable ID token, or an error response from the provider), and every one of those failures ends on the server side with nowhere to report it. The agreed contract is that the server redirects back to
/login?error=oauth2_callback_failed, and this page turns that parameter into a visible message so a failed sign-in does not dead-end on a blank page. This issue only consumes the parameter; emitting it belongs to the server work.The workspace shell (
SidebarProvider+AppSidebar) currently wraps every route inApp.tsx. An unauthenticated teacher has no apps to navigate to, so/loginrenders outside that shell.Acceptance criteria
Teacher starts Edupass sign-in from the login page
/login/auth/edupass, leaving the single-page app rather than handling the click in the client routerLogin page renders outside the workspace sidebar shell
/loginat any viewport widthFailed Edupass sign-in is reported on the login page
/login?error=oauth2_callback_failedLogin page shows no error on a normal visit
/loginwith noerrorparameter, an emptyerrorparameter, or anerrorvalue other thanoauth2_callback_failedWorkspace routes are unaffected by the login page
/,/students,/posts,/groups, or an unknown pathOut of scope
/auth/edupassroute and the entire Edupass OIDC exchange (feat(server/auth): add the Edupass OIDC client and login routes #75). This page only links to the route; it is not created or changed here./login?error=oauth2_callback_failedredirect. The server side of that contract belongs to feat(server/auth): add the Edupass OIDC client and login routes #75. This issue only reads the parameter.oauth2_callback_failed. Any other value renders no error./login./login. There is no client-visible session state to check yet.Design assets
Prototype: https://teacherworkspace-prototype.string.sg/login (build the Edupass button only; omit the demo account).
Route structure after this change:
flowchart LR A["/login"] -->|"Sign in with Edupass"| B["GET /auth/edupass"] B -.->|"sign-in fails (#75)"| C["/login?error=oauth2_callback_failed"] C --> A B -.->|"sign-in succeeds (#75)"| D["/"]Technical context
Pending grooming.
Data model
Pending grooming.
API contract
Pending grooming.
Error contract
Pending grooming.
Additional test scenarios
Pending grooming.
Hard constraints
Pending grooming.
🤖 Generated with aif-create-issue