Skip to content
Merged

OAuth #628

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e4dd20b
startup now requires a client-id for oauth
mewilker Dec 29, 2025
e96329c
successfully retrieves an api token
mewilker Dec 31, 2025
a645adb
add dummy to docker compose for client id
mewilker Jan 6, 2026
dab83ee
pull openid data from the source and read the jwt token for identity
mewilker Jan 6, 2026
122ed07
Rename CasController.java to RedirectController and updated documenta…
mewilker Jan 6, 2026
0fb9199
Rename CasService to Authentiaction Service as CAS protocol is no lon…
mewilker Jan 7, 2026
f10d921
fix documentation typo
mewilker Jan 7, 2026
ff14d71
add logic for multiple keys
mewilker Jan 7, 2026
1bf1866
add documentation to getting-started.md
mewilker Jan 8, 2026
9fdce15
improve validation for AuthenticationService.java
mewilker Jan 8, 2026
f897f64
move appropriate get request logic to NetworkUtils
mewilker Jan 8, 2026
72a3eba
fix url encoding
mewilker Jan 8, 2026
a89cdb9
throw unauthorized exception when the callback is not called with a c…
mewilker Jan 8, 2026
54cc3d3
remove unused code and improve documentation
mewilker Jan 8, 2026
497119c
move the status code check into the function that makes the request
mewilker Jan 8, 2026
708838a
extract all possible networking logic
mewilker Jan 8, 2026
47b5edc
create AuthenticationService unit tests
mewilker Jan 14, 2026
f300702
add test for authorization url
mewilker Jan 14, 2026
4de5a34
fix not checking if cache time was above 0
mewilker Jan 14, 2026
b163302
create key pair helper functions for testing
mewilker Jan 16, 2026
426d5cc
fix readJWKs to not return anything
mewilker Jan 20, 2026
0dfe754
add helper function to generate token with given key
mewilker Jan 20, 2026
550ae2c
fix static issues introduced within JwtUtils
mewilker Jan 20, 2026
580a62b
add unit tests to JwtUtils for multiple keys
mewilker Jan 20, 2026
68182dd
fix imports and redundant variables
mewilker Jan 20, 2026
e023be5
Merge branch 'main' into oauth
mewilker Jan 20, 2026
930ef7c
remove commented old code
mewilker Jan 21, 2026
89fa61a
specify that a sandbox client id is needed
mewilker Jan 22, 2026
4ce2e33
change netId to follow naming conventions
mewilker Jan 22, 2026
ba5609b
change locator logic for readability
mewilker Jan 22, 2026
155805e
fix difference between class and file name
mewilker Jan 22, 2026
fb9ef71
documentation grammar fix
mewilker Jan 22, 2026
de15ffc
fix key parser redundant variable
mewilker Jan 27, 2026
61f96ad
create refresh config function
mewilker Jan 27, 2026
26b6b1a
invert if else logic in cacheBYUOpenIDConfig
mewilker Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ services:
"--canvas-token", "changeme",
"--use-canvas", "true",
# "--disable-compilation", # Enable me, if desired!
"--client-id", "changeme",
]
networks:
- autograder
Expand Down
6 changes: 6 additions & 0 deletions docs/getting-started/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ Do the following actions:
# Use one of the following, but not both
--canvas-token <canvas api key>
--use-canvas false

#Follow the steps at https://developer.byu.edu/data/api-usage/create-an-oauth-client
#Please choose the sandbox environment
#You are going to want to choose the Auth Code + PKCE option
#For the redirect url, you should use the cas-callback-url
--client-id <client id>
```

### 6. Run the Autograder Locally
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ private static void setupProperties(String[] args) {
if (cmd.hasOption("disable-compilation")) {
properties.setProperty("run-compilation", "false");
}
if(cmd.hasOption("client-id")){
properties.setProperty("client-id", cmd.getOptionValue("client-id"));
}
} catch (ParseException e) {
throw new RuntimeException("Error parsing command line arguments", e);
}
Expand All @@ -109,6 +112,7 @@ private static Options getOptions() {
options.addOption(null, "canvas-token", true, "Canvas Token");
options.addOption(null, "use-canvas", true, "Using Canvas");
options.addOption(null, "disable-compilation", false, "Turn off student code compilation");
options.addOption(null, "client-id", true, "Client ID for BYU OAuth");
return options;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,71 @@
package edu.byu.cs.controller;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import edu.byu.cs.canvas.CanvasException;
import edu.byu.cs.controller.exception.UnauthorizedException;
import edu.byu.cs.dataAccess.DataAccessException;
import edu.byu.cs.model.User;
import edu.byu.cs.properties.ApplicationProperties;
import edu.byu.cs.service.CasService;
import edu.byu.cs.service.AuthenticationService;
import edu.byu.cs.service.ConfigService;
import static edu.byu.cs.util.JwtUtils.generateToken;
import io.javalin.http.Context;
import io.javalin.http.Cookie;
import io.javalin.http.Handler;
import io.javalin.http.HttpStatus;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import static edu.byu.cs.util.JwtUtils.generateToken;

/**
* Handles CAS-related HTTP endpoints. CAS, standing for <em>Central Authentication Service</em>,
* is BYU's centralized authentication provider for all BYU users
* Handles Redirect related endpoints, including the class chat link (i. e. Slack or Discord)
* and authentication redirects.
*/
public class CasController {
public class RedirectController {

public static final Handler callbackGet = ctx -> {
String ticket = ctx.queryParam("ticket");
String code = ctx.queryParam("code");
if (code == null){
throw new UnauthorizedException();
}
AuthenticationService.TokenResponse response = AuthenticationService.exchangeCodeForTokens(code);

User user;
try {
user = CasService.callback(ticket);
user = AuthenticationService.callback(response.idToken());
} catch (CanvasException e) {
String errorUrlParam = URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8);
ctx.redirect(ApplicationProperties.frontendUrl() + "/login?error=" + errorUrlParam, HttpStatus.FOUND);
return;
}

// FIXME: secure cookie with httpOnly
ctx.cookie("token", generateToken(user.netId()), 14400);
ctx.cookie (new Cookie(
"token",
generateToken(user.netId()),
"/",
14400,
AuthenticationService.isSecure(),
0,
true
));

redirect(ctx);
};



public static final Handler loginGet = ctx -> {
// check if already logged in
if (ctx.cookie("token") != null) {
redirect(ctx);
return;
}
ctx.redirect(CasService.BYU_CAS_URL + "/login" + "?service=" + ApplicationProperties.casCallbackUrl());
ctx.redirect(AuthenticationService.getAuthorizationUrl());
};


/**
* Redirects students to the class chat invite. At the time we used Slack, and therefore all references use
* that name
*/
private static void redirect(Context ctx) throws DataAccessException {
String redirectTo;
if(ctx.sessionAttribute("slack") != null) {
Expand All @@ -64,7 +82,7 @@ private static void redirect(Context ctx) throws DataAccessException {
return;
}

// TODO: call cas logout endpoint with ticket
// TODO: call logout endpoint with token
ctx.removeCookie("token", "/");
ctx.redirect(ApplicationProperties.frontendUrl(), HttpStatus.OK);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public static String frontendUrl() {
return mustGet("frontend-url");
}

public static String clientId() {return mustGet("client-id");}

public static String casCallbackUrl() {
return mustGet("cas-callback-url");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,17 @@ public Handler meGet() {

@Override
public Handler callbackGet() {
return CasController.callbackGet;
return RedirectController.callbackGet;
}

@Override
public Handler loginGet() {
return CasController.loginGet;
return RedirectController.loginGet;
}

@Override
public Handler logoutPost() {
return CasController.logoutPost;
return RedirectController.logoutPost;
}

// ConfigController
Expand Down
Loading