Skip to content

Commit 467bf65

Browse files
authored
Merge pull request #31 from nemanjam/feature/github-login-article
GitHub login with FastAPI and Next.js - article
2 parents 726f093 + 76e0d82 commit 467bf65

15 files changed

Lines changed: 879 additions & 260 deletions

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"@astrojs/sitemap": "^3.7.0",
4646
"@astrojs/tailwind": "^5.1.5",
4747
"@fontsource-variable/inter": "^5.2.8",
48-
"astro": "^5.16.15",
48+
"astro": "^5.17.1",
4949
"astro-embed": "^0.12.0",
5050
"astro-expressive-code": "^0.41.6",
5151
"astro-icon": "^1.1.5",
@@ -54,16 +54,16 @@
5454
"class-variance-authority": "^0.7.1",
5555
"clsx": "^2.1.1",
5656
"date-fns": "^4.1.0",
57-
"dotenv": "^17.2.3",
57+
"dotenv": "^17.2.4",
5858
"feed": "^5.2.0",
5959
"giscus": "^1.6.0",
6060
"github-slugger": "^2.0.0",
6161
"lodash.debounce": "^4.0.8",
6262
"mdast-util-to-string": "^4.0.0",
6363
"object-treeify": "^5.0.1",
6464
"photoswipe": "^5.4.4",
65-
"react": "^19.2.3",
66-
"react-dom": "^19.2.3",
65+
"react": "^19.2.4",
66+
"react-dom": "^19.2.4",
6767
"react-icons": "^5.5.0",
6868
"reading-time": "^1.5.0",
6969
"rehype-external-links": "^3.0.0",
@@ -75,15 +75,15 @@
7575
"devDependencies": {
7676
"@astrojs/check": "^0.9.6",
7777
"@expressive-code/plugin-collapsible-sections": "^0.41.6",
78-
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
78+
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
7979
"@iconify-json/mdi": "^1.2.3",
8080
"@tailwindcss/typography": "^0.5.19",
8181
"@types/lodash.debounce": "^4.0.9",
8282
"@types/mdast": "^4.0.4",
83-
"@types/react": "^19.2.9",
83+
"@types/react": "^19.2.13",
8484
"@types/react-dom": "^19.2.3",
85-
"@typescript-eslint/eslint-plugin": "^8.53.1",
86-
"@typescript-eslint/parser": "^8.53.1",
85+
"@typescript-eslint/eslint-plugin": "^8.54.0",
86+
"@typescript-eslint/parser": "^8.54.0",
8787
"eslint": "8.57.0",
8888
"eslint-config-prettier": "^10.1.8",
8989
"eslint-mdx": "^3.6.2",

pnpm-lock.yaml

Lines changed: 289 additions & 252 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/constants/collections.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const TAGS = [
2828
'algorithms',
2929
'computer-science',
3030
'fastapi',
31+
'authentication',
3132
] as const;
3233

3334
/** adjust this later */

src/content/post/2026/01-03-nextjs-server-actions-fastapi-openapi/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ The relevant files:
672672

673673
```bash
674674
git clone git@github.com:nemanjam/full-stack-fastapi-template-nextjs.git
675+
git checkout 4442faf066b34b3c625ae4a31a3761f5dc0517f9
675676

676677
# Backend
677678
backend/app/core/security.py
73.1 KB
Loading
80.7 KB
Loading
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0" version="29.3.7">
2+
<diagram name="Page-1" id="MTQAKK2TWoGsr5AXjv3s">
3+
<mxGraphModel grid="1" page="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
4+
<root>
5+
<mxCell id="0" />
6+
<mxCell id="1" parent="0" />
7+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-5" parent="1" style="shape=module;align=left;spacingLeft=20;align=center;verticalAlign=top;whiteSpace=wrap;html=1;fontStyle=1" value="Next.js server" vertex="1">
8+
<mxGeometry height="360" width="250" x="310" y="80" as="geometry" />
9+
</mxCell>
10+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-6" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;" value="&lt;b&gt;Server Action&lt;/b&gt;" vertex="1">
11+
<mxGeometry height="90" width="180" x="350" y="115" as="geometry" />
12+
</mxCell>
13+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-7" parent="oZtozJSmbuDn1Zi4SEUL-6" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
14+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
15+
<mxPoint x="-27" y="7" as="offset" />
16+
</mxGeometry>
17+
</mxCell>
18+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-8" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;" value="&lt;b&gt;API route&lt;/b&gt;" vertex="1">
19+
<mxGeometry height="90" width="180" x="350" y="320" as="geometry" />
20+
</mxCell>
21+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-9" parent="oZtozJSmbuDn1Zi4SEUL-8" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
22+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
23+
<mxPoint x="-27" y="7" as="offset" />
24+
</mxGeometry>
25+
</mxCell>
26+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-23" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-8" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;edgeStyle=orthogonalEdgeStyle;" target="oZtozJSmbuDn1Zi4SEUL-26" value="">
27+
<mxGeometry height="50" relative="1" width="50" as="geometry">
28+
<mxPoint x="166" y="267" as="sourcePoint" />
29+
<mxPoint x="166" y="191" as="targetPoint" />
30+
</mxGeometry>
31+
</mxCell>
32+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-26" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;align=center;" value="&lt;div align=&quot;center&quot;&gt;&lt;b&gt;Browser&lt;/b&gt;&lt;/div&gt;" vertex="1">
33+
<mxGeometry height="80" width="170" x="80" y="80" as="geometry" />
34+
</mxCell>
35+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-27" parent="oZtozJSmbuDn1Zi4SEUL-26" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
36+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
37+
<mxPoint x="-27" y="7" as="offset" />
38+
</mxGeometry>
39+
</mxCell>
40+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-28" parent="1" style="shape=module;align=left;spacingLeft=20;align=center;verticalAlign=top;whiteSpace=wrap;html=1;fontStyle=1" value="FastAPI server" vertex="1">
41+
<mxGeometry height="560" width="250" x="630" y="80" as="geometry" />
42+
</mxCell>
43+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-29" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;" value="&lt;b&gt;Login endpoint&lt;/b&gt;" vertex="1">
44+
<mxGeometry height="90" width="180" x="670" y="115" as="geometry" />
45+
</mxCell>
46+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-30" parent="oZtozJSmbuDn1Zi4SEUL-29" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
47+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
48+
<mxPoint x="-27" y="7" as="offset" />
49+
</mxGeometry>
50+
</mxCell>
51+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-31" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;" value="&lt;b&gt;Redirect endpoint&lt;/b&gt;" vertex="1">
52+
<mxGeometry height="90" width="180" x="670" y="318" as="geometry" />
53+
</mxCell>
54+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-32" parent="oZtozJSmbuDn1Zi4SEUL-31" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
55+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
56+
<mxPoint x="-27" y="7" as="offset" />
57+
</mxGeometry>
58+
</mxCell>
59+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-33" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;align=center;" value="&lt;div align=&quot;center&quot;&gt;&lt;b&gt;Database&lt;/b&gt;&lt;/div&gt;" vertex="1">
60+
<mxGeometry height="80" width="170" x="961" y="117.03" as="geometry" />
61+
</mxCell>
62+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-34" parent="oZtozJSmbuDn1Zi4SEUL-33" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
63+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
64+
<mxPoint x="-27" y="7" as="offset" />
65+
</mxGeometry>
66+
</mxCell>
67+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-35" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;align=center;" value="&lt;div align=&quot;center&quot;&gt;&lt;b&gt;GitHub&lt;/b&gt;&lt;/div&gt;" vertex="1">
68+
<mxGeometry height="80" width="170" x="961" y="319" as="geometry" />
69+
</mxCell>
70+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-36" parent="oZtozJSmbuDn1Zi4SEUL-35" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
71+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
72+
<mxPoint x="-27" y="7" as="offset" />
73+
</mxGeometry>
74+
</mxCell>
75+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-37" parent="1" style="html=1;dropTarget=0;whiteSpace=wrap;" value="&lt;b&gt;Callback endpoint&lt;/b&gt;" vertex="1">
76+
<mxGeometry height="90" width="180" x="670" y="520" as="geometry" />
77+
</mxCell>
78+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-38" parent="oZtozJSmbuDn1Zi4SEUL-37" style="shape=module;jettyWidth=8;jettyHeight=4;" value="" vertex="1">
79+
<mxGeometry height="20" relative="1" width="20" x="1" as="geometry">
80+
<mxPoint x="-27" y="7" as="offset" />
81+
</mxGeometry>
82+
</mxCell>
83+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-39" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Set cookie" vertex="1">
84+
<mxGeometry height="20" width="217" x="150" y="365" as="geometry" />
85+
</mxCell>
86+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-40" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-6" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;edgeStyle=orthogonalEdgeStyle;" target="oZtozJSmbuDn1Zi4SEUL-26" value="">
87+
<mxGeometry height="50" relative="1" width="50" as="geometry">
88+
<mxPoint x="220" y="438" as="sourcePoint" />
89+
<mxPoint x="220" y="332" as="targetPoint" />
90+
</mxGeometry>
91+
</mxCell>
92+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-41" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="&lt;div&gt;Set/Unset cookie&lt;/div&gt;" vertex="1">
93+
<mxGeometry height="20" width="217" x="181" y="181" as="geometry" />
94+
</mxCell>
95+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-42" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-6" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" target="oZtozJSmbuDn1Zi4SEUL-29" value="">
96+
<mxGeometry height="50" relative="1" width="50" as="geometry">
97+
<mxPoint x="280" y="430" as="sourcePoint" />
98+
<mxPoint x="280" y="324" as="targetPoint" />
99+
</mxGeometry>
100+
</mxCell>
101+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-43" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="email/password" vertex="1">
102+
<mxGeometry height="20" width="217" x="480" y="138" as="geometry" />
103+
</mxCell>
104+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-44" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-29" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" target="oZtozJSmbuDn1Zi4SEUL-6" value="">
105+
<mxGeometry height="50" relative="1" width="50" as="geometry">
106+
<mxPoint x="200" y="446" as="sourcePoint" />
107+
<mxPoint x="200" y="340" as="targetPoint" />
108+
</mxGeometry>
109+
</mxCell>
110+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-45" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="access_token, expires" vertex="1">
111+
<mxGeometry height="20" width="217" x="490" y="183" as="geometry" />
112+
</mxCell>
113+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-46" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-37" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;exitX=0;exitY=0.75;exitDx=0;exitDy=0;edgeStyle=orthogonalEdgeStyle;" target="oZtozJSmbuDn1Zi4SEUL-8" value="">
114+
<mxGeometry height="50" relative="1" width="50" as="geometry">
115+
<mxPoint x="200" y="456" as="sourcePoint" />
116+
<mxPoint x="200" y="350" as="targetPoint" />
117+
</mxGeometry>
118+
</mxCell>
119+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-47" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="access_token, expires" vertex="1">
120+
<mxGeometry height="20" width="217" x="470" y="588" as="geometry" />
121+
</mxCell>
122+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-49" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-31" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;edgeStyle=orthogonalEdgeStyle;elbow=vertical;" target="oZtozJSmbuDn1Zi4SEUL-35" value="">
123+
<mxGeometry height="50" relative="1" width="50" as="geometry">
124+
<mxPoint x="190" y="456" as="sourcePoint" />
125+
<mxPoint x="190" y="350" as="targetPoint" />
126+
</mxGeometry>
127+
</mxCell>
128+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-53" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Client ID, Client secret" vertex="1">
129+
<mxGeometry height="20" width="217" x="806" y="340" as="geometry" />
130+
</mxCell>
131+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-54" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-35" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;edgeStyle=entityRelationEdgeStyle;" target="oZtozJSmbuDn1Zi4SEUL-37" value="">
132+
<mxGeometry height="50" relative="1" width="50" as="geometry">
133+
<mxPoint x="190" y="486" as="sourcePoint" />
134+
<mxPoint x="190" y="380" as="targetPoint" />
135+
</mxGeometry>
136+
</mxCell>
137+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-55" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="User profile" vertex="1">
138+
<mxGeometry height="20" width="217" x="850" y="400" as="geometry" />
139+
</mxCell>
140+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-57" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-37" style="endArrow=block;startArrow=block;endFill=1;startFill=1;html=1;rounded=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;exitX=1;exitY=0.75;exitDx=0;exitDy=0;edgeStyle=orthogonalEdgeStyle;" target="oZtozJSmbuDn1Zi4SEUL-33" value="">
141+
<mxGeometry relative="1" width="160" as="geometry">
142+
<mxPoint x="200" y="400" as="sourcePoint" />
143+
<mxPoint x="360" y="400" as="targetPoint" />
144+
</mxGeometry>
145+
</mxCell>
146+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-59" parent="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Find/create user" vertex="1">
147+
<mxGeometry height="20" width="217" x="820" y="588" as="geometry" />
148+
</mxCell>
149+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-60" edge="1" parent="1" source="oZtozJSmbuDn1Zi4SEUL-29" style="endArrow=block;startArrow=block;endFill=1;startFill=1;html=1;rounded=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;" target="oZtozJSmbuDn1Zi4SEUL-33" value="">
150+
<mxGeometry relative="1" width="160" as="geometry">
151+
<mxPoint x="210" y="400" as="sourcePoint" />
152+
<mxPoint x="370" y="400" as="targetPoint" />
153+
</mxGeometry>
154+
</mxCell>
155+
<mxCell id="oZtozJSmbuDn1Zi4SEUL-61" parent="1" style="text;align=center;fontStyle=1;verticalAlign=middle;spacingLeft=3;spacingRight=3;strokeColor=none;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;html=1;fontSize=16;" value="&lt;div&gt;Email/password and OAuth login&lt;/div&gt;" vertex="1">
156+
<mxGeometry height="20" width="130" x="193.5" y="520" as="geometry" />
157+
</mxCell>
158+
</root>
159+
</mxGraphModel>
160+
</diagram>
161+
</mxfile>
73.1 KB
Loading

src/content/post/2026/02-07-github-login-fastapi-nextjs/_resources/github-login-architecture-16-9.svg

Lines changed: 4 additions & 0 deletions
Loading
26.6 KB
Loading

0 commit comments

Comments
 (0)