Skip to content

Commit 9f0ebd4

Browse files
authored
Merge pull request #38 from geode-sdk/camila
add /v1/login/github/token for extremely simple Github Action CI integration
2 parents 090b92d + 9d92e90 commit 9f0ebd4

File tree

4 files changed

+124
-71
lines changed

4 files changed

+124
-71
lines changed

openapi.yml

+55-3
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,14 @@ paths:
8383
content:
8484
application/json:
8585
schema:
86-
type: string
87-
description: The token used for authentication
88-
example: "a1b2c3d4-1234-5678-9abc-1234567890ab"
86+
type: object
87+
properties:
88+
error:
89+
type: "null"
90+
payload:
91+
type: string
92+
description: The token used for authentication
93+
example: "a1b2c3d4-1234-5678-9abc-1234567890ab"
8994
"400":
9095
description: Bad request - for different reasons, like IP mismatch, interval not being respected, etc.
9196
content:
@@ -100,6 +105,53 @@ paths:
100105
"500":
101106
$ref: "#/components/responses/InternalServerError"
102107

108+
/v1/login/github/token:
109+
post:
110+
tags:
111+
- user
112+
summary: Login with GitHub access token
113+
description: Log in immediately with a GitHub access token. Returns a token if the login was successful.
114+
requestBody:
115+
content:
116+
application/json:
117+
schema:
118+
type: object
119+
properties:
120+
token:
121+
type: string
122+
description: The access token from GitHub
123+
example: "ghp_1234567890abcdefgh"
124+
responses:
125+
"200":
126+
description: OK
127+
content:
128+
application/json:
129+
schema:
130+
type: object
131+
properties:
132+
error:
133+
type: "null"
134+
payload:
135+
type: object
136+
properties:
137+
type: string
138+
description: The Geode token used for authentication
139+
example: "a1b2c3d4-1234-5678-9abc-1234567890ab"
140+
"400":
141+
description: Bad request - invalid access token.
142+
content:
143+
application/json:
144+
schema:
145+
type: object
146+
properties:
147+
error:
148+
type: string
149+
payload:
150+
type: "null"
151+
"500":
152+
$ref: "#/components/responses/InternalServerError"
153+
154+
103155
/v1/mods:
104156
get:
105157
tags:

src/auth/github.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ impl GithubClient {
164164
}
165165
}
166166

167-
pub async fn get_user(&self, token: String) -> Result<serde_json::Value, ApiError> {
167+
pub async fn get_user(&self, token: &str) -> Result<serde_json::Value, ApiError> {
168168
let client = Client::new();
169169
let resp = match client
170170
.get("https://api.github.com/user")

src/endpoints/auth/github.rs

+67-67
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use actix_web::{dev::ConnectionInfo, post, web, Responder};
22
use serde::Deserialize;
3-
use sqlx::{types::ipnetwork::IpNetwork, Acquire};
3+
use sqlx::{types::ipnetwork::IpNetwork, Acquire, PgConnection};
44
use uuid::Uuid;
55

66
use crate::{
@@ -17,6 +17,26 @@ struct PollParams {
1717
uuid: String,
1818
}
1919

20+
#[derive(Deserialize)]
21+
struct TokenLoginParams {
22+
token: String,
23+
}
24+
25+
async fn developer_from_token(
26+
pool: &mut PgConnection,
27+
user: serde_json::Value
28+
) -> Result<Uuid, Option<ApiError>> {
29+
let id = user.get("id").ok_or(None)?.as_i64().unwrap();
30+
let username = user.get("login").ok_or(None)?;
31+
32+
let dev_id = match Developer::get_by_github_id(id, pool).await? {
33+
Some(x) => x.id,
34+
None => Developer::create(id, username.to_string(), pool).await?
35+
};
36+
37+
create_token_for_developer(dev_id, pool).await.map_err(Some)
38+
}
39+
2040
#[post("v1/login/github")]
2141
pub async fn start_github_login(
2242
data: web::Data<AppData>,
@@ -138,17 +158,6 @@ pub async fn poll_github_login(
138158
log::error!("{}", e);
139159
return Err(ApiError::InternalError);
140160
};
141-
let user = match client.get_user(token).await {
142-
Err(e) => {
143-
transaction
144-
.rollback()
145-
.await
146-
.or(Err(ApiError::TransactionError))?;
147-
log::error!("{}", e);
148-
return Err(ApiError::InternalError);
149-
}
150-
Ok(u) => u,
151-
};
152161

153162
// Create a new transaction after this point, because we need to commit the removal of the login attempt
154163

@@ -157,71 +166,62 @@ pub async fn poll_github_login(
157166
.await
158167
.or(Err(ApiError::TransactionError))?;
159168

169+
let user = client.get_user(&token).await.map_err(|_| ApiError::InternalError)?;
160170
let mut transaction = pool.begin().await.or(Err(ApiError::TransactionError))?;
161171

162-
let id = match user.get("id") {
163-
None => return Err(ApiError::InternalError),
164-
Some(id) => id.as_i64().unwrap(),
165-
};
166-
if let Some(x) = Developer::get_by_github_id(id, &mut transaction).await? {
167-
let token = match create_token_for_developer(x.id, &mut transaction).await {
168-
Err(_) => {
169-
transaction
170-
.rollback()
171-
.await
172-
.or(Err(ApiError::TransactionError))?;
173-
return Err(ApiError::InternalError);
174-
}
175-
Ok(t) => t,
176-
};
177-
transaction
178-
.commit()
179-
.await
180-
.or(Err(ApiError::TransactionError))?;
181-
return Ok(web::Json(ApiResponse {
182-
error: "".to_string(),
183-
payload: token.to_string(),
184-
}));
185-
}
186-
let username = match user.get("login") {
187-
None => {
188-
transaction
189-
.rollback()
190-
.await
191-
.or(Err(ApiError::TransactionError))?;
192-
return Err(ApiError::InternalError);
193-
}
194-
Some(user) => user.to_string(),
195-
};
196-
let id = match Developer::create(id, username, &mut transaction).await {
172+
let token = match developer_from_token(&mut transaction, user).await {
173+
Ok(t) => t,
197174
Err(e) => {
198-
transaction
199-
.rollback()
200-
.await
201-
.or(Err(ApiError::TransactionError))?;
202-
log::error!("{}", e);
203-
return Err(ApiError::InternalError);
175+
if let Some(e) = e {
176+
log::error!("{}", e);
177+
}
178+
179+
transaction.rollback().await.map_or_else(
180+
|_| Err(ApiError::TransactionError),
181+
|_| Err(ApiError::InternalError)
182+
)?
204183
}
205-
Ok(i) => i,
206184
};
207-
let token = match create_token_for_developer(id, &mut transaction).await {
185+
186+
Ok(web::Json(ApiResponse {
187+
error: "".to_string(),
188+
payload: token.to_string(),
189+
}))
190+
}
191+
192+
#[post("v1/login/github/token")]
193+
pub async fn github_token_login(
194+
json: web::Json<TokenLoginParams>,
195+
data: web::Data<AppData>,
196+
) -> Result<impl Responder, ApiError> {
197+
let client = github::GithubClient::new(
198+
data.github_client_id.to_string(),
199+
data.github_client_secret.to_string(),
200+
);
201+
202+
let user = client.get_user(&json.token).await
203+
.map_err(|_| ApiError::BadRequest(format!("Invalid access token: {}", json.token)))?;
204+
205+
let mut pool = data.db.acquire().await.or(Err(ApiError::DbAcquireError))?;
206+
let mut transaction = pool.begin().await.or(Err(ApiError::TransactionError))?;
207+
208+
let token = match developer_from_token(&mut transaction, user).await {
209+
Ok(t) => t,
210+
208211
Err(e) => {
209-
transaction
210-
.rollback()
211-
.await
212-
.or(Err(ApiError::TransactionError))?;
213-
log::error!("{}", e);
214-
return Err(ApiError::InternalError);
212+
if let Some(e) = e {
213+
log::error!("{}", e);
214+
}
215+
216+
transaction.rollback().await.map_or_else(
217+
|_| Err(ApiError::TransactionError),
218+
|_| Err(ApiError::InternalError)
219+
)?
215220
}
216-
Ok(t) => t,
217221
};
218-
transaction
219-
.commit()
220-
.await
221-
.or(Err(ApiError::TransactionError))?;
222222

223223
Ok(web::Json(ApiResponse {
224224
error: "".to_string(),
225-
payload: token.to_string(),
225+
payload: token.to_string()
226226
}))
227227
}

src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ async fn main() -> anyhow::Result<()> {
119119
.service(endpoints::mod_versions::create_version)
120120
.service(endpoints::mod_versions::update_version)
121121
.service(endpoints::auth::github::poll_github_login)
122+
.service(endpoints::auth::github::github_token_login)
122123
.service(endpoints::auth::github::start_github_login)
123124
.service(endpoints::developers::developer_index)
124125
.service(endpoints::developers::get_developer)

0 commit comments

Comments
 (0)