Skip to content

Commit 3c80113

Browse files
Merge pull request #276 from pilcrowonpaper/next
Release v3.3.0
2 parents a00c075 + 1ebf685 commit 3c80113

File tree

7 files changed

+152
-2
lines changed

7 files changed

+152
-2
lines changed

.RELEASE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add Gitea provider ([#265](https://github.com/pilcrowonpaper/arctic/pull/265)).

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Arctic does not strictly follow semantic versioning. While we aim to only introd
5050
- Epic Games
5151
- Facebook
5252
- Figma
53+
- Gitea
5354
- GitHub
5455
- GitLab
5556
- Google

docs/malta.config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
["Etsy", "/providers/etsy"],
3737
["Facebook", "/providers/facebook"],
3838
["Figma", "/providers/figma"],
39+
["Gitea", "/providers/gitea"],
3940
["GitHub", "/providers/github"],
4041
["GitLab", "/providers/gitlab"],
4142
["Google", "/providers/google"],

docs/pages/providers/gitea.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: "Gitea"
3+
---
4+
5+
# Gitea
6+
7+
OAuth 2.0 provider for Gitea.
8+
9+
Also see [OAuth 2.0 with PKCE](/guides/oauth2-pkce).
10+
11+
## Initialization
12+
13+
The `baseURL` parameter is the full URL where the Gitea instance is hosted. Use `https://gitea.com` for managed servers. Pass the client secret for confidential clients.
14+
15+
```ts
16+
import * as arctic from "arctic";
17+
18+
const baseURL = "https://gitea.com";
19+
const baseURL = "https://my-app.com/gitea";
20+
const gitea = new arctic.gitea(baseURL, clientId, clientSecret, redirectURI);
21+
const gitea = new arctic.gitea(baseURL, clientId, null, redirectURI);
22+
```
23+
24+
## Create authorization URL
25+
26+
```ts
27+
import * as arctic from "arctic";
28+
29+
const state = arctic.generateState();
30+
const codeVerifier = arctic.generateCodeVerifier();
31+
const scopes = ["read:user", "write:notification"];
32+
const url = gitea.createAuthorizationURL(state, codeVerifier, scopes);
33+
```
34+
35+
## Validate authorization code
36+
37+
`validateAuthorizationCode()` will either return an [`OAuth2Tokens`](/reference/main/OAuth2Tokens), or throw one of [`OAuth2RequestError`](/reference/main/OAuth2RequestError), [`ArcticFetchError`](/reference/main/ArcticFetchError), [`UnexpectedResponseError`](/reference/main/UnexpectedResponseError), or [`UnexpectedErrorResponseBodyError`](/reference/main/UnexpectedErrorResponseBodyError). Gitea returns an access token, the access token expiration, and a refresh token.
38+
39+
```ts
40+
import * as arctic from "arctic";
41+
42+
try {
43+
const tokens = await gitea.validateAuthorizationCode(code, codeVerifier);
44+
const accessToken = tokens.accessToken();
45+
const accessTokenExpiresAt = tokens.accessTokenExpiresAt();
46+
const refreshToken = tokens.refreshToken();
47+
} catch (e) {
48+
if (e instanceof arctic.OAuth2RequestError) {
49+
// Invalid authorization code, credentials, or redirect URI
50+
const code = e.code;
51+
// ...
52+
}
53+
if (e instanceof arctic.ArcticFetchError) {
54+
// Failed to call `fetch()`
55+
const cause = e.cause;
56+
// ...
57+
}
58+
// Parse error
59+
}
60+
```
61+
62+
## Refresh access tokens
63+
64+
Use `refreshAccessToken()` to get a new access token using a refresh token. This method's behavior is identical to `validateAuthorizationCode()`.
65+
66+
```ts
67+
import * as arctic from "arctic";
68+
69+
try {
70+
const tokens = await gitea.refreshAccessToken(refreshToken);
71+
const accessToken = tokens.accessToken();
72+
const accessTokenExpiresAt = tokens.accessTokenExpiresAt();
73+
const refreshToken = tokens.refreshToken();
74+
} catch (e) {
75+
if (e instanceof arctic.OAuth2RequestError) {
76+
// Invalid authorization code, credentials, or redirect URI
77+
}
78+
if (e instanceof arctic.ArcticFetchError) {
79+
// Failed to call `fetch()`
80+
}
81+
// Parse error
82+
}
83+
```
84+
85+
## Get user profile
86+
87+
Add the `read:user` scope and use the [`/user` endpoint](https://gitea.com/api/swagger#/user).
88+
89+
```ts
90+
const scopes = ["read:user"];
91+
const url = gitea.createAuthorizationURL(state, codeVerifier, scopes);
92+
```
93+
94+
```ts
95+
const response = await fetch("https://gitea.com/user", {
96+
headers: {
97+
Authorization: `Bearer ${accessToken}`
98+
}
99+
});
100+
const user = await response.json();
101+
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "arctic",
33
"type": "module",
4-
"version": "3.2.4",
4+
"version": "3.3.0",
55
"description": "OAuth 2.0 clients for popular providers",
66
"main": "dist/index.js",
77
"types": "dist/index.d.ts",

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ export { Etsy } from "./providers/etsy.js";
1616
export { EpicGames } from "./providers/epicgames.js";
1717
export { Facebook } from "./providers/facebook.js";
1818
export { Figma } from "./providers/figma.js";
19-
export { Intuit } from "./providers/intuit.js";
19+
export { Gitea } from "./providers/gitea.js";
2020
export { GitHub } from "./providers/github.js";
2121
export { GitLab } from "./providers/gitlab.js";
2222
export { Google } from "./providers/google.js";
23+
export { Intuit } from "./providers/intuit.js";
2324
export { Kakao } from "./providers/kakao.js";
2425
export { KeyCloak } from "./providers/keycloak.js";
2526
export { Lichess } from "./providers/lichess.js";

src/providers/gitea.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { CodeChallengeMethod, OAuth2Client } from "../client.js";
2+
import { joinURIAndPath } from "../request.js";
3+
4+
import type { OAuth2Tokens } from "../oauth2.js";
5+
6+
export class Gitea {
7+
private authorizationEndpoint: string;
8+
private tokenEndpoint: string;
9+
10+
private client: OAuth2Client;
11+
12+
constructor(baseURL: string, clientId: string, clientSecret: string | null, redirectURI: string) {
13+
this.authorizationEndpoint = joinURIAndPath(baseURL, "/login/oauth/authorize");
14+
this.tokenEndpoint = joinURIAndPath(baseURL, "/login/oauth/access_token");
15+
this.client = new OAuth2Client(clientId, clientSecret, redirectURI);
16+
}
17+
18+
public createAuthorizationURL(state: string, codeVerifier: string, scopes: string[]): URL {
19+
const url = this.client.createAuthorizationURLWithPKCE(
20+
this.authorizationEndpoint,
21+
state,
22+
CodeChallengeMethod.S256,
23+
codeVerifier,
24+
scopes
25+
);
26+
return url;
27+
}
28+
29+
public async validateAuthorizationCode(
30+
code: string,
31+
codeVerifier: string
32+
): Promise<OAuth2Tokens> {
33+
const tokens = await this.client.validateAuthorizationCode(
34+
this.tokenEndpoint,
35+
code,
36+
codeVerifier
37+
);
38+
return tokens;
39+
}
40+
41+
public async refreshAccessToken(refreshToken: string): Promise<OAuth2Tokens> {
42+
const tokens = await this.client.refreshAccessToken(this.tokenEndpoint, refreshToken, []);
43+
return tokens;
44+
}
45+
}

0 commit comments

Comments
 (0)