diff --git a/src/lib/utils/references.ts b/src/lib/utils/references.ts index 11d828d46..4beebf36d 100644 --- a/src/lib/utils/references.ts +++ b/src/lib/utils/references.ts @@ -65,6 +65,7 @@ export const Platform = { type PlatformType = typeof Platform; export type Platform = (typeof Platform)[keyof typeof Platform]; +export const VALID_PLATFORMS = new Set(Object.values(Platform)); export const Framework = { NextJs: 'Next.js', @@ -154,9 +155,17 @@ export const preferredVersion = writable( globalThis?.localStorage?.getItem('preferredVersion') as Version ); -export const preferredPlatform = writable( - (globalThis?.localStorage?.getItem('preferredPlatform') ?? 'client-web') as Platform -); +function getInitialPlatform(): Platform { + const stored = globalThis?.localStorage?.getItem('preferredPlatform') ?? Platform.ClientWeb; + // return if this platform is valid + if (VALID_PLATFORMS.has(stored as Platform)) { + return stored as Platform; + } else { + return Platform.ClientWeb; + } +} + +export const preferredPlatform = writable(getInitialPlatform()); if (browser) { preferredVersion.subscribe((value) => { @@ -164,6 +173,9 @@ if (browser) { }); preferredPlatform.subscribe((value) => { - if (value) globalThis?.localStorage?.setItem('preferredPlatform', value); + // only save the ones for which we have api references. + if (value && VALID_PLATFORMS.has(value)) { + globalThis?.localStorage?.setItem('preferredPlatform', value); + } }); } diff --git a/src/markdoc/nodes/Heading.svelte b/src/markdoc/nodes/Heading.svelte index cc1a2332f..37f2c1f8f 100644 --- a/src/markdoc/nodes/Heading.svelte +++ b/src/markdoc/nodes/Heading.svelte @@ -81,5 +81,5 @@ class:web-snap-location-references={id && inReferences} class="{headingClass} text-primary scroll-m-32 font-medium" > - {@render children()} + {@render children()} diff --git a/src/routes/blog/post/understanding-idp-vs-sp-initiated-sso/+page.markdoc b/src/routes/blog/post/understanding-idp-vs-sp-initiated-sso/+page.markdoc new file mode 100644 index 000000000..caf6a97a0 --- /dev/null +++ b/src/routes/blog/post/understanding-idp-vs-sp-initiated-sso/+page.markdoc @@ -0,0 +1,110 @@ +--- +layout: post +title: "Understanding IdP vs SP-Initiated SSO" +description: A quick guide to IdP vs SP-initiated SSO and when to use each. +date: 2025-06-16 +cover: /images/blog/understanding-idp-vs-sp-initiated-sso/cover.png +timeToRead: 06 +author: laura-du-ry +callToAction: true +unlisted: true +category: product +--- + +Managing authentication across multiple applications is a growing challenge for developers, especially with users expecting more convenience and security. Single Sign-On (SSO) offers a practical solution to that problem, allowing users to access multiple services with one login. Although the experience is almost always seamless for users, developers have multiple options for implementing SSO in their applications. + +This guide breaks down the differences between **Identity Provider (IdP)-initiated** and **Service Provider (SP)-initiated** SSO, their advantages and trade-offs, and how to choose the best fit for your setup. + +# What is IdP-Initiated SSO? + +First, a quick refresher: an **Identity Provider (IdP)** manages user identities, validating who a user is before granting access to different applications. Here’s a quick [overview](/docs/products/auth/identities) of how Appwrite handles identity and access. + +In an IdP-initiated SSO flow, the user’s journey starts at the IdP itself: + +# How it works + +1. User logs in to the IdP. +2. The IdP displays a dashboard of connected applications. +3. The user selects a service to access. +4. The IdP sends a secure authentication token (such as a SAML assertion) to the Service Provider (SP). +5. The SP grants access based on the [token](/docs/products/auth/tokens). + +# Advantages + +- **Streamlined access**: Launch multiple services from a single dashboard. +- **Reduced credential reuse**: Minimizes repeated logins, lowering the risk of compromised credentials. +- **Centralized control**: Simplifies user monitoring and access management. + +# Trade-offs + +- **Extra navigation step**: Users must first visit the IdP portal. +- **Single point of failure**: If the IdP is compromised, multiple services could be at risk. +- **Integration challenges**: Some services may not fully support IdP-initiated workflows. + +{% call_to_action title="Customer identity without the hassle" description="Add secure authentication for your users in just a couple of minutes." point1="GDPR, HIPAA and SOC 2 compliant" point2="Built-in security" point3="Multi-factor authentication" point4="Integrates with your favourite SDK" cta="Contact sales" url="/contact-us/enterprise" /%} + +# What is SP-Initiated SSO? + +**Service Providers (SPs)** are the applications or services users want to access. + +In SP-initiated SSO, the process begins when a user attempts to log into an application directly: + +# How it works + +1. User tries to access the service. +2. The service detects no active session and redirects the user to the IdP. +3. The user authenticates at the IdP. +4. The IdP sends an authentication token back to the service. +5. The service grants access. + +# Advantages + +- **Direct access**: Users can go straight to the service they want. +- **Seamless integration**: Fits naturally into user-driven workflows. +- **Flexibility**: Useful for both internal and external users. + +# Trade-offs + +- **Redirect dependency**: Requires smooth coordination between service and IdP. +- **Increased setup complexity**: Proper configuration is critical to avoid login issues. + +# IdP- vs SP-Initiated SSO: Quick Comparison + +| Feature | IdP-Initiated SSO | SP-Initiated SSO | +| --- | --- | --- | +| **Starting Point** | Identity Provider portal | Service Provider login page | +| **User Flow** | Login at IdP, then select services | Attempt service access, then authenticate via IdP | +| **User Experience** | Best for environments with multiple services | Best for quick, direct service access | +| **Security Considerations** | Central control but single point of vulnerability | Stronger per-service session security | +| **Typical Use Cases** | Corporate portals, education hubs | SaaS apps, customer-facing platforms | + +# When to choose IdP-Initiated SSO + +- **Organizations with many internal services**: Ideal for centralized portals. +- **Formal environments**: Where users are accustomed to navigating through a unified dashboard. +- **Legacy system compatibility**: Easier integration with older systems. + +# When to Choose SP-Initiated SSO + +- **User-first services**: Where users need to quickly access a single app. +- **B2B and B2C platforms**: Especially when users might come in via bookmarks, emails, or direct links. +- **Dynamic environments**: Where new apps are frequently added or removed. + +Pro tip: SP-initiated flows are often complemented by [adaptive MFA](/docs/products/auth/mfa) to enhance security without compromising the user experience. + +# When to use both approaches + +Many organizations implement both IdP- and SP-initiated SSO to serve different user needs: + +- **Employee and partner ecosystems**: Employees might use IdP dashboards while partners or customers prefer direct access. +- **Hybrid cloud setups**: Supporting a mix of legacy and modern applications. +- **Adaptive security strategies**: Choosing the flow based on device, location, or user profile. + +Choosing the right SSO initiation method,or blending both, can dramatically impact [security](/docs/products/auth/security), user satisfaction, and scalability. Evaluate your platform's user behavior, security posture, and integration needs to pick the best approach for your environment. + +# Futher reading + +- [Appwrite Authentication docs](/docs/products/auth) +- [Developer's guide to user authentication](/blog/post/guide-to-user-authentication) +- [Appwrite Authentication overview](/products/auth) + diff --git a/src/routes/docs/advanced/platform/+layout.svelte b/src/routes/docs/advanced/platform/+layout.svelte index dbb64f0d2..32f230029 100644 --- a/src/routes/docs/advanced/platform/+layout.svelte +++ b/src/routes/docs/advanced/platform/+layout.svelte @@ -152,6 +152,11 @@ new: isNewUntil('28 Feb 2025'), label: 'Abuse', href: '/docs/advanced/platform/abuse' + }, + { + new: isNewUntil('31 July 2025'), + label: 'Support SLA', + href: '/docs/advanced/platform/support-sla' } ] } diff --git a/src/routes/docs/advanced/platform/support-sla/+page.markdoc b/src/routes/docs/advanced/platform/support-sla/+page.markdoc new file mode 100644 index 000000000..bdc1d95df --- /dev/null +++ b/src/routes/docs/advanced/platform/support-sla/+page.markdoc @@ -0,0 +1,54 @@ +--- +layout: article +title: Support SLA +description: Learn about Appwrite's support service level agreement (SLA) including response times, severity levels, and support commitments for different subscription tiers. +--- + +This Support Service Level Agreement ("SLA") describes the support services provided by APPWRITE ("we," "us," or "our") to users of our products and services ("you" or "user"). By using our services, you agree to the terms of this SLA. + +## Scope + +This SLA outlines our commitments for providing support services via email, including response and resolution processes based on issue severity. The specific response times depend on the support tier associated with your subscription: **Pro**, **Scale**, or **Enterprise**. + +## Severity levels + +Support issues are categorized into the following severity levels: + +- **Critical**: System is down or a critical component is non-functional, causing a complete stoppage of work or significant business impact. +- **High**: Major functionality is impaired, but a workaround is available, or a critical component is significantly degraded. +- **Medium**: Minor functionality is impaired without significant business impact. +- **Low**: Issue has minor impact on business operations; workaround is not necessary. +- **Question**: Requests for information, general guidance, or feature requests. + +## Response time targets + +| Severity | Pro | Scale | Enterprise | +| --- | --- | --- | --- | +| Critical | Unsupported | 1 hour (24/7/365) | 15 minutes (24/7/365) | +| High | Unsupported | 4 hours (24/7/365) | 1 hour (24/7/365) | +| Medium | 2 business days | 1 business day | 12 hours (24/7/365) | +| Low | 3 business days | 2 business days | 24 hours (24/7/365) | +| Question | 4 business days | 3 business days | 1 business day | + +## Business hours and days + +Our standard business hours are from **9:00 AM to 5:00 PM Pacific Time**, Monday through Friday, excluding public holidays. Enterprise and Scale customers receive extended support 24/7/365. + +## User responsibilities + +To ensure effective support, users are expected to: + +- Provide detailed information about each issue, including screenshots, error messages, logs, and steps to reproduce the problem. +- Ensure relevant personnel are available to assist in diagnosing and resolving issues. +- Implement reasonable recommendations provided by our support team. + +## Limitations and exclusions + +- This SLA applies only to support requests submitted via the Appwrite Console. +- SLA obligations may be affected by factors outside our reasonable control, including but not limited to force majeure events, third-party dependencies, or actions taken by the user. + +## Modifications + +We reserve the right to modify this SLA at any time. Changes become effective upon posting to our website. Your continued use of our services after changes indicates your acceptance of the updated SLA. + +For questions or concerns about this SLA, please contact us at our [contact page](https://appwrite.io/contact-us). \ No newline at end of file diff --git a/src/routes/docs/products/auth/server-side-rendering/+page.markdoc b/src/routes/docs/products/auth/server-side-rendering/+page.markdoc index 242d928b3..f42873a3a 100644 --- a/src/routes/docs/products/auth/server-side-rendering/+page.markdoc +++ b/src/routes/docs/products/auth/server-side-rendering/+page.markdoc @@ -72,6 +72,17 @@ $adminClient = (new Client()) ->setKey(''); // Your secret API key +``` +```python +from appwrite.client import Client + +admin_client = (Client() + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint \ + .set_project('') # Your project ID + .set_key('') # Your secret API key + ) + + ``` {% /multicode %} @@ -105,6 +116,22 @@ if ($session) { $sessionClient->setSession($session); } ``` + +```python +from flask import request +from appwrite.client import Client + +session_client = (Client() + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + ) + +# Get the session cookie from the request +session = request.cookies.get('session') +if session: + session_client.set_session(session) + +``` {% /multicode %} # Creating email/password sessions {% #creating-sessions %} @@ -178,6 +205,39 @@ try { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } ``` +```python +from flask import Flask, request, jsonify, make_response + +# Initialize admin client here +# ... + +@app.post('/login') +def login(): + body = request.json + # Get email and password from request + email = body['email'] + password = body['password'] + + try: + account = Account(admin_client) + + # Create the session using the Appwrite client + session = account.create_email_password_session(email, password) + resp = make_response(jsonify({'success': True})) + + # Set the session cookie + resp.set_cookie('session', + session['secret'], + httponly=True, + secure=True, + samesite='Strict', + expires=session['expire'], + path='/' + ) + return resp + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 400 +``` {% /multicode %} We also recommend using the `httpOnly`, `secure`, and `sameSite` cookie options to ensure that the cookie is only sent over HTTPS, @@ -242,6 +302,30 @@ try { } catch (Exception $e) { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } +``` +```python +# Initialize the session client here + +@app.get('/user') +def get_user(): + # First, read the session cookie from the request + session = request.cookies.get('session') + + # If the session cookie is not present, return an error + if not session: + return jsonify({'success': False, 'error': 'Unauthorized'}), 401 + + # pass the session cookie to the Appwrite client + session_client.set_session(session) + account = Account(session_client) + + # Now, you can make authenticated requests to the Appwrite API + try: + user = account.get() + return jsonify({'success': True, 'user': user}) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 400 + ``` {% /multicode %} @@ -319,6 +403,19 @@ $account = new Account($client); $result = $account->createAnonymousSession(); ``` +```python +from appwrite.client import Client +from appwrite.services.account import Account + +client = (Client() + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + ) + +account = Account(client) + +result = account.create_anonymous_session() +``` {% /multicode %} # Forwarding user agent {% #forwarding-user-agent %} @@ -333,6 +430,9 @@ client.setForwardedUserAgent(req.headers['user-agent']); setForwardedUserAgent($_SERVER['HTTP_USER_AGENT']); ``` +```python +client.set_forwarded_user_agent(request.headers.get('user-agent')) +``` {% /multicode %} # OAuth2 {% #oauth2 %} @@ -383,6 +483,29 @@ $redirectUrl = $account->createOAuth2Token( header('Location' . $redirectUrl); ``` +```python +from appwrite.client import Client +from appwrite.services.account import Account, OAuthProvider +from flask import Flask, request ,redirect, make_response, jsonify + +admin_client = (Client() + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + ) + +@app.get('/oauth') +def oauth(): + account = Account(admin_client) + + redirect_url = account.create_o_auth2_token( + OAuthProvider.Github, # Provider + 'https://example.com/oauth/success', # Success URL + 'https://example.com/oauth/failure', # Failure URL + ) + + return redirect(redirect_url) +``` {% /multicode %} Next, create a success callback endpoint that receives the `userId` and `secret` URL parameters, and then calls `createSession` on the server side. This endpoint returns a session object, which you can store in a cookie. @@ -448,6 +571,38 @@ try { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } ``` +```python +@app.get('/oauth/success') +def oauth_success(): + account = Account(admin_client) + + # Get the userId and secret from the URL parameters + user_id = request.args.get('userId') + secret = request.args.get('secret') + + try: + # Create the session using the Appwrite client + session = account.create_session(user_id, secret) + + # Set the session cookie + res = make_response(jsonify({'success': True})) + + # Set session cookie + res.set_cookie( + 'session', + session['secret'], + httponly=True, + secure=True, + samesite='Strict', + max_age=session['expire'], + path='/' + ) + + return res + + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 400 +``` {% /multicode %} Now the cookie is set, it will be passed to the server with subsequent requests, and you can use it to make authenticated requests to the Appwrite API on behalf of the end-user. diff --git a/src/routes/docs/references/[version]/models/[model]/+page.svelte b/src/routes/docs/references/[version]/models/[model]/+page.svelte index 756d4295a..89de3a008 100644 --- a/src/routes/docs/references/[version]/models/[model]/+page.svelte +++ b/src/routes/docs/references/[version]/models/[model]/+page.svelte @@ -41,7 +41,7 @@ {property.name} {property.type} - {property.description} + {@html parse(property.description)} {#if property.relatedModels} Can be one of: {@html parse(property.relatedModels)} diff --git a/src/routes/partners/(components)/benefits.svelte b/src/routes/partners/(components)/benefits.svelte index 3b415a8f9..30d57b1cf 100644 --- a/src/routes/partners/(components)/benefits.svelte +++ b/src/routes/partners/(components)/benefits.svelte @@ -20,7 +20,7 @@ { title: 'Training', description: - 'We provide in depth training and workshops to help you master Appwrite for your clients.', + 'We provide in-depth training and workshops to help you master Appwrite for your clients.', icon: Training }, { @@ -36,9 +36,9 @@ icon: EarlyAccess }, { - title: 'Revenue share', + title: 'Innovation', description: - 'For each client you sell Appwrite to, you will receive a part of the revenue for a whole year.', + "Empower your team and elevate your customers' experiences with the newest technology.", icon: Revenue }, { @@ -64,7 +64,7 @@ }, { title: 'All in one platform', - description: 'All the APIs a developer needs in one place. And more to come.', + description: 'Everything you need to develop, deploy, and scale your applications.', icon: Expert } ]; diff --git a/static/images/blog/understanding-idp-vs-sp-initiated-sso/cover.png b/static/images/blog/understanding-idp-vs-sp-initiated-sso/cover.png new file mode 100644 index 000000000..fff47bdca Binary files /dev/null and b/static/images/blog/understanding-idp-vs-sp-initiated-sso/cover.png differ