the37lab-authlib 0.1.1750156111__tar.gz → 0.1.1750836881__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of the37lab-authlib might be problematic. Click here for more details.

Files changed (15) hide show
  1. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/PKG-INFO +24 -16
  2. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/README.md +23 -15
  3. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/pyproject.toml +1 -1
  4. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib/auth.py +33 -42
  5. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib.egg-info/PKG-INFO +24 -16
  6. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/setup.cfg +0 -0
  7. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib/__init__.py +0 -0
  8. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib/db.py +0 -0
  9. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib/decorators.py +0 -0
  10. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib/exceptions.py +0 -0
  11. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib/models.py +0 -0
  12. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib.egg-info/SOURCES.txt +0 -0
  13. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib.egg-info/dependency_links.txt +0 -0
  14. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib.egg-info/requires.txt +0 -0
  15. {the37lab_authlib-0.1.1750156111 → the37lab_authlib-0.1.1750836881}/src/the37lab_authlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: the37lab_authlib
3
- Version: 0.1.1750156111
3
+ Version: 0.1.1750836881
4
4
  Summary: Python SDK for the Authlib
5
5
  Author-email: the37lab <info@the37lab.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -72,6 +72,12 @@ def protected_route():
72
72
  return "Protected content"
73
73
  ```
74
74
 
75
+ `AuthManager`'s blueprint now registers a global error handler for
76
+ `AuthError` and authenticates requests for all of its routes by default.
77
+ Authenticated users are made available as `flask.g.requesting_user`.
78
+ Only the login, OAuth, token refresh, registration and role listing
79
+ endpoints are exempt from this check.
80
+
75
81
  ## Configuration
76
82
 
77
83
  ### Required Parameters
@@ -101,50 +107,52 @@ def protected_route():
101
107
  ## API Endpoints
102
108
 
103
109
  ### Authentication
104
- - `POST /v1/users/login` - Login with username/password
110
+ - `POST /api/v1/users/login` - Login with username/password
105
111
  - **Request:** `{ "username": "string", "password": "string" }`
106
112
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
107
- - `POST /v1/users/login/oauth` - Get OAuth redirect URL
113
+ - `POST /api/v1/users/login/oauth` - Get OAuth redirect URL
108
114
  - **Request:** `{ "provider": "google|github|..." }`
109
115
  - **Response:** `{ "redirect_url": "string" }`
110
- - `GET /v1/users/login/oauth2callback` - OAuth callback
116
+ - `GET /api/v1/users/login/oauth2callback` - OAuth callback
111
117
  - **Query Params:** `code`, `state`, `provider`
112
118
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
113
- - `POST /v1/users/token-refresh` - Refresh JWT token
119
+ - `POST /api/v1/users/token-refresh` - Refresh JWT token
114
120
  - **Request:** `{ "refresh_token": "jwt" }`
115
121
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt" }`
116
122
 
117
123
  ### User Management
118
- - `POST /v1/users/register` - Register new user
124
+ - `POST /api/v1/users/register` - Register new user
119
125
  - **Request:** `{ "username": "string", "password": "string", "email": "string", ... }`
120
126
  - **Response:** `{ "user": { ... }, "token": "jwt", "refresh_token": "jwt" }`
121
- - `GET /v1/users/login/profile` - Get user profile
127
+ - `GET /api/v1/users/login/profile` - Get user profile
122
128
  - **Auth:** Bearer JWT
123
129
  - **Response:** `{ "user": { ... } }`
124
- - `GET /v1/users/roles` - Get available roles
130
+ - `GET /api/v1/users/roles` - Get available roles
125
131
  - **Response:** `[ "admin", "user", ... ]`
126
132
 
127
133
  ### API Tokens
128
- - `POST /v1/users/{user}/api-tokens` - Create API token
134
+ - `POST /api/v1/users/{user}/api-tokens` - Create API token
129
135
  - **Request:** `{ "name": "string", "scopes": [ ... ] }`
130
136
  - **Response:** `{ "token": "string", "id": "uuid", ... }`
131
- - `GET /v1/users/{user}/api-tokens` - List API tokens
137
+ - `GET /api/v1/users/{user}/api-tokens` - List API tokens
132
138
  - **Response:** `[ { "id": "uuid", "name": "string", ... } ]`
133
- - `DELETE /v1/users/{user}/api-tokens/{token_id}` - Delete API token
139
+ - `DELETE /api/v1/users/{user}/api-tokens/{token_id}` - Delete API token
134
140
  - **Response:** `{ "success": true }`
135
141
 
136
142
  ## Authentication Flow
137
143
 
138
144
  1. **Login:**
139
- - User submits credentials to `/v1/users/login`.
145
+ - User submits credentials to `/api/v1/users/login`.
140
146
  - Receives JWT and refresh token.
141
147
  2. **Token Refresh:**
142
- - Use `/v1/users/token-refresh` with refresh token to get new JWT.
148
+ - Use `/api/v1/users/token-refresh` with refresh token to get new JWT.
143
149
  3. **OAuth:**
144
- - Get redirect URL from `/v1/users/login/oauth`.
145
- - Complete OAuth flow via `/v1/users/login/oauth2callback`.
150
+ - Get redirect URL from `/api/v1/users/login/oauth`.
151
+ - Complete OAuth flow via `/api/v1/users/login/oauth2callback`.
146
152
  4. **Protected Routes:**
147
- - Use `@auth.require_auth()` decorator to protect Flask routes.
153
+ - All routes inside the provided blueprint are authenticated by default.
154
+ The authenticated user can be accessed via `g.requesting_user`.
155
+ Use `@auth.require_auth()` to protect custom routes in your application.
148
156
 
149
157
  ## User Object
150
158
 
@@ -55,6 +55,12 @@ def protected_route():
55
55
  return "Protected content"
56
56
  ```
57
57
 
58
+ `AuthManager`'s blueprint now registers a global error handler for
59
+ `AuthError` and authenticates requests for all of its routes by default.
60
+ Authenticated users are made available as `flask.g.requesting_user`.
61
+ Only the login, OAuth, token refresh, registration and role listing
62
+ endpoints are exempt from this check.
63
+
58
64
  ## Configuration
59
65
 
60
66
  ### Required Parameters
@@ -84,50 +90,52 @@ def protected_route():
84
90
  ## API Endpoints
85
91
 
86
92
  ### Authentication
87
- - `POST /v1/users/login` - Login with username/password
93
+ - `POST /api/v1/users/login` - Login with username/password
88
94
  - **Request:** `{ "username": "string", "password": "string" }`
89
95
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
90
- - `POST /v1/users/login/oauth` - Get OAuth redirect URL
96
+ - `POST /api/v1/users/login/oauth` - Get OAuth redirect URL
91
97
  - **Request:** `{ "provider": "google|github|..." }`
92
98
  - **Response:** `{ "redirect_url": "string" }`
93
- - `GET /v1/users/login/oauth2callback` - OAuth callback
99
+ - `GET /api/v1/users/login/oauth2callback` - OAuth callback
94
100
  - **Query Params:** `code`, `state`, `provider`
95
101
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
96
- - `POST /v1/users/token-refresh` - Refresh JWT token
102
+ - `POST /api/v1/users/token-refresh` - Refresh JWT token
97
103
  - **Request:** `{ "refresh_token": "jwt" }`
98
104
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt" }`
99
105
 
100
106
  ### User Management
101
- - `POST /v1/users/register` - Register new user
107
+ - `POST /api/v1/users/register` - Register new user
102
108
  - **Request:** `{ "username": "string", "password": "string", "email": "string", ... }`
103
109
  - **Response:** `{ "user": { ... }, "token": "jwt", "refresh_token": "jwt" }`
104
- - `GET /v1/users/login/profile` - Get user profile
110
+ - `GET /api/v1/users/login/profile` - Get user profile
105
111
  - **Auth:** Bearer JWT
106
112
  - **Response:** `{ "user": { ... } }`
107
- - `GET /v1/users/roles` - Get available roles
113
+ - `GET /api/v1/users/roles` - Get available roles
108
114
  - **Response:** `[ "admin", "user", ... ]`
109
115
 
110
116
  ### API Tokens
111
- - `POST /v1/users/{user}/api-tokens` - Create API token
117
+ - `POST /api/v1/users/{user}/api-tokens` - Create API token
112
118
  - **Request:** `{ "name": "string", "scopes": [ ... ] }`
113
119
  - **Response:** `{ "token": "string", "id": "uuid", ... }`
114
- - `GET /v1/users/{user}/api-tokens` - List API tokens
120
+ - `GET /api/v1/users/{user}/api-tokens` - List API tokens
115
121
  - **Response:** `[ { "id": "uuid", "name": "string", ... } ]`
116
- - `DELETE /v1/users/{user}/api-tokens/{token_id}` - Delete API token
122
+ - `DELETE /api/v1/users/{user}/api-tokens/{token_id}` - Delete API token
117
123
  - **Response:** `{ "success": true }`
118
124
 
119
125
  ## Authentication Flow
120
126
 
121
127
  1. **Login:**
122
- - User submits credentials to `/v1/users/login`.
128
+ - User submits credentials to `/api/v1/users/login`.
123
129
  - Receives JWT and refresh token.
124
130
  2. **Token Refresh:**
125
- - Use `/v1/users/token-refresh` with refresh token to get new JWT.
131
+ - Use `/api/v1/users/token-refresh` with refresh token to get new JWT.
126
132
  3. **OAuth:**
127
- - Get redirect URL from `/v1/users/login/oauth`.
128
- - Complete OAuth flow via `/v1/users/login/oauth2callback`.
133
+ - Get redirect URL from `/api/v1/users/login/oauth`.
134
+ - Complete OAuth flow via `/api/v1/users/login/oauth2callback`.
129
135
  4. **Protected Routes:**
130
- - Use `@auth.require_auth()` decorator to protect Flask routes.
136
+ - All routes inside the provided blueprint are authenticated by default.
137
+ The authenticated user can be accessed via `g.requesting_user`.
138
+ Use `@auth.require_auth()` to protect custom routes in your application.
131
139
 
132
140
  ## User Object
133
141
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "the37lab_authlib"
7
- version = "0.1.1750156111"
7
+ version = "0.1.1750836881"
8
8
  description = "Python SDK for the Authlib"
9
9
  authors = [{name = "the37lab", email = "info@the37lab.com"}]
10
10
  dependencies = ["flask", "psycopg2-binary", "pyjwt", "python-dotenv", "requests", "authlib", "bcrypt"]
@@ -1,5 +1,5 @@
1
1
  import inspect
2
- from flask import Blueprint, request, jsonify, current_app, url_for, redirect
2
+ from flask import Blueprint, request, jsonify, current_app, url_for, redirect, g
3
3
  import jwt
4
4
  from datetime import datetime, timedelta
5
5
  from .db import Database
@@ -15,17 +15,6 @@ from functools import wraps
15
15
  logging.basicConfig(level=logging.DEBUG)
16
16
  logger = logging.getLogger(__name__)
17
17
 
18
- def handle_auth_errors(f):
19
- @wraps(f)
20
- def decorated(*args, **kwargs):
21
- try:
22
- return f(*args, **kwargs)
23
- except AuthError as e:
24
- response = jsonify(e.to_dict())
25
- response.status_code = e.status_code
26
- return response
27
- return decorated
28
-
29
18
  class AuthManager:
30
19
  def __init__(self, app=None, db_dsn=None, jwt_secret=None, oauth_config=None, id_type='integer'):
31
20
  logger.info("INITIALIZING AUTHMANAGER {} - {} - {}".format(db_dsn, jwt_secret, not app))
@@ -130,10 +119,29 @@ class AuthManager:
130
119
  app.register_blueprint(self.create_blueprint())
131
120
 
132
121
  def create_blueprint(self):
133
- bp = Blueprint('auth', __name__, url_prefix='/v1/users')
122
+ bp = Blueprint('auth', __name__, url_prefix='/api/v1/users')
123
+
124
+ public_endpoints = {
125
+ 'auth.login',
126
+ 'auth.oauth_login',
127
+ 'auth.oauth_callback',
128
+ 'auth.refresh_token',
129
+ 'auth.register',
130
+ 'auth.get_roles'
131
+ }
132
+
133
+ @bp.errorhandler(AuthError)
134
+ def handle_auth_error(err):
135
+ response = jsonify(err.to_dict())
136
+ response.status_code = err.status_code
137
+ return response
138
+
139
+ @bp.before_request
140
+ def load_user():
141
+ if request.endpoint not in public_endpoints:
142
+ g.requesting_user = self._authenticate_request()
134
143
 
135
144
  @bp.route('/login', methods=['POST'])
136
- @handle_auth_errors
137
145
  def login():
138
146
  data = request.get_json()
139
147
  username = data.get('username')
@@ -168,7 +176,6 @@ class AuthManager:
168
176
  })
169
177
 
170
178
  @bp.route('/login/oauth', methods=['POST'])
171
- @handle_auth_errors
172
179
  def oauth_login():
173
180
  provider = request.json.get('provider')
174
181
  if provider not in self.oauth_config:
@@ -180,7 +187,6 @@ class AuthManager:
180
187
  })
181
188
 
182
189
  @bp.route('/login/oauth2callback')
183
- @handle_auth_errors
184
190
  def oauth_callback():
185
191
  code = request.args.get('code')
186
192
  provider = request.args.get('state')
@@ -197,28 +203,22 @@ class AuthManager:
197
203
  return redirect(f"{frontend_url}/oauth-callback?token={token}&refresh_token={refresh_token}")
198
204
 
199
205
  @bp.route('/login/profile')
200
- @handle_auth_errors
201
206
  def profile():
202
- token = request.headers.get('Authorization', '').split(' ')[-1]
203
- user = self.validate_token(token)
207
+ user = g.requesting_user
204
208
  return jsonify(user)
205
209
 
206
210
  @bp.route('/api-tokens', methods=['GET'])
207
- @handle_auth_errors
208
- @self.require_auth
209
- def get_tokens(requesting_user):
210
- tokens = self.get_user_api_tokens(requesting_user['id'])
211
+ def get_tokens():
212
+ tokens = self.get_user_api_tokens(g.requesting_user['id'])
211
213
  return jsonify(tokens)
212
214
 
213
215
  @bp.route('/api-tokens', methods=['POST'])
214
- @handle_auth_errors
215
- @self.require_auth
216
- def create_token(requesting_user):
216
+ def create_token():
217
217
  name = request.json.get('name')
218
218
  expires_in_days = request.json.get('expires_in_days')
219
219
  if not name:
220
220
  raise AuthError('Token name is required', 400)
221
- api_token = self.create_api_token(requesting_user['id'], name, expires_in_days)
221
+ api_token = self.create_api_token(g.requesting_user['id'], name, expires_in_days)
222
222
  return jsonify({
223
223
  'id': api_token.id,
224
224
  'name': api_token.name,
@@ -228,7 +228,6 @@ class AuthManager:
228
228
  })
229
229
 
230
230
  @bp.route('/token-refresh', methods=['POST'])
231
- @handle_auth_errors
232
231
  def refresh_token():
233
232
  refresh_token = request.json.get('refresh_token')
234
233
  if not refresh_token:
@@ -253,20 +252,16 @@ class AuthManager:
253
252
  raise AuthError('Invalid refresh token', 401)
254
253
 
255
254
  @bp.route('/api-tokens', methods=['POST'])
256
- @handle_auth_errors
257
- @self.require_auth
258
- def create_api_token(requesting_user):
255
+ def create_api_token():
259
256
  name = request.json.get('name')
260
257
  if not name:
261
258
  raise AuthError('Token name required', 400)
262
259
 
263
- token = self.create_api_token(requesting_user['id'], name)
260
+ token = self.create_api_token(g.requesting_user['id'], name)
264
261
  return jsonify({'token': token.token})
265
262
 
266
263
  @bp.route('/api-tokens/validate', methods=['GET'])
267
- @handle_auth_errors
268
- @self.require_auth
269
- def validate_api_token(requesting_user):
264
+ def validate_api_token():
270
265
  token = request.json.get('token')
271
266
  if not token:
272
267
  raise AuthError('No API token provided', 401)
@@ -276,7 +271,7 @@ class AuthManager:
276
271
  cur.execute("""
277
272
  SELECT * FROM api_tokens
278
273
  WHERE user_id = %s AND id = %s
279
- """, (requesting_user['id'], token))
274
+ """, (g.requesting_user['id'], token))
280
275
  api_token = cur.fetchone()
281
276
 
282
277
  if not api_token:
@@ -297,9 +292,7 @@ class AuthManager:
297
292
  return jsonify({'valid': True})
298
293
 
299
294
  @bp.route('/api-tokens', methods=['DELETE'])
300
- @handle_auth_errors
301
- @self.require_auth
302
- def delete_api_token(requesting_user):
295
+ def delete_api_token():
303
296
  token = request.json.get('token')
304
297
  if not token:
305
298
  raise AuthError('Token required', 400)
@@ -310,7 +303,7 @@ class AuthManager:
310
303
  DELETE FROM api_tokens
311
304
  WHERE user_id = %s AND id = %s
312
305
  RETURNING id
313
- """, (requesting_user['id'], token))
306
+ """, (g.requesting_user['id'], token))
314
307
  deleted_id = cur.fetchone()
315
308
  if not deleted_id:
316
309
  raise ValueError('Token not found or already deleted')
@@ -318,7 +311,6 @@ class AuthManager:
318
311
  return jsonify({'deleted': True})
319
312
 
320
313
  @bp.route('/register', methods=['POST'])
321
- @handle_auth_errors
322
314
  def register():
323
315
  data = request.get_json()
324
316
 
@@ -357,7 +349,6 @@ class AuthManager:
357
349
  return jsonify({'id': user.id}), 201
358
350
 
359
351
  @bp.route('/roles', methods=['GET'])
360
- @handle_auth_errors
361
352
  def get_roles():
362
353
  with self.db.get_cursor() as cur:
363
354
  cur.execute("SELECT * FROM roles")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: the37lab_authlib
3
- Version: 0.1.1750156111
3
+ Version: 0.1.1750836881
4
4
  Summary: Python SDK for the Authlib
5
5
  Author-email: the37lab <info@the37lab.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -72,6 +72,12 @@ def protected_route():
72
72
  return "Protected content"
73
73
  ```
74
74
 
75
+ `AuthManager`'s blueprint now registers a global error handler for
76
+ `AuthError` and authenticates requests for all of its routes by default.
77
+ Authenticated users are made available as `flask.g.requesting_user`.
78
+ Only the login, OAuth, token refresh, registration and role listing
79
+ endpoints are exempt from this check.
80
+
75
81
  ## Configuration
76
82
 
77
83
  ### Required Parameters
@@ -101,50 +107,52 @@ def protected_route():
101
107
  ## API Endpoints
102
108
 
103
109
  ### Authentication
104
- - `POST /v1/users/login` - Login with username/password
110
+ - `POST /api/v1/users/login` - Login with username/password
105
111
  - **Request:** `{ "username": "string", "password": "string" }`
106
112
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
107
- - `POST /v1/users/login/oauth` - Get OAuth redirect URL
113
+ - `POST /api/v1/users/login/oauth` - Get OAuth redirect URL
108
114
  - **Request:** `{ "provider": "google|github|..." }`
109
115
  - **Response:** `{ "redirect_url": "string" }`
110
- - `GET /v1/users/login/oauth2callback` - OAuth callback
116
+ - `GET /api/v1/users/login/oauth2callback` - OAuth callback
111
117
  - **Query Params:** `code`, `state`, `provider`
112
118
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt", "user": { ... } }`
113
- - `POST /v1/users/token-refresh` - Refresh JWT token
119
+ - `POST /api/v1/users/token-refresh` - Refresh JWT token
114
120
  - **Request:** `{ "refresh_token": "jwt" }`
115
121
  - **Response:** `{ "token": "jwt", "refresh_token": "jwt" }`
116
122
 
117
123
  ### User Management
118
- - `POST /v1/users/register` - Register new user
124
+ - `POST /api/v1/users/register` - Register new user
119
125
  - **Request:** `{ "username": "string", "password": "string", "email": "string", ... }`
120
126
  - **Response:** `{ "user": { ... }, "token": "jwt", "refresh_token": "jwt" }`
121
- - `GET /v1/users/login/profile` - Get user profile
127
+ - `GET /api/v1/users/login/profile` - Get user profile
122
128
  - **Auth:** Bearer JWT
123
129
  - **Response:** `{ "user": { ... } }`
124
- - `GET /v1/users/roles` - Get available roles
130
+ - `GET /api/v1/users/roles` - Get available roles
125
131
  - **Response:** `[ "admin", "user", ... ]`
126
132
 
127
133
  ### API Tokens
128
- - `POST /v1/users/{user}/api-tokens` - Create API token
134
+ - `POST /api/v1/users/{user}/api-tokens` - Create API token
129
135
  - **Request:** `{ "name": "string", "scopes": [ ... ] }`
130
136
  - **Response:** `{ "token": "string", "id": "uuid", ... }`
131
- - `GET /v1/users/{user}/api-tokens` - List API tokens
137
+ - `GET /api/v1/users/{user}/api-tokens` - List API tokens
132
138
  - **Response:** `[ { "id": "uuid", "name": "string", ... } ]`
133
- - `DELETE /v1/users/{user}/api-tokens/{token_id}` - Delete API token
139
+ - `DELETE /api/v1/users/{user}/api-tokens/{token_id}` - Delete API token
134
140
  - **Response:** `{ "success": true }`
135
141
 
136
142
  ## Authentication Flow
137
143
 
138
144
  1. **Login:**
139
- - User submits credentials to `/v1/users/login`.
145
+ - User submits credentials to `/api/v1/users/login`.
140
146
  - Receives JWT and refresh token.
141
147
  2. **Token Refresh:**
142
- - Use `/v1/users/token-refresh` with refresh token to get new JWT.
148
+ - Use `/api/v1/users/token-refresh` with refresh token to get new JWT.
143
149
  3. **OAuth:**
144
- - Get redirect URL from `/v1/users/login/oauth`.
145
- - Complete OAuth flow via `/v1/users/login/oauth2callback`.
150
+ - Get redirect URL from `/api/v1/users/login/oauth`.
151
+ - Complete OAuth flow via `/api/v1/users/login/oauth2callback`.
146
152
  4. **Protected Routes:**
147
- - Use `@auth.require_auth()` decorator to protect Flask routes.
153
+ - All routes inside the provided blueprint are authenticated by default.
154
+ The authenticated user can be accessed via `g.requesting_user`.
155
+ Use `@auth.require_auth()` to protect custom routes in your application.
148
156
 
149
157
  ## User Object
150
158