stac_extensions/authentication.rs
1//! Provides a standard set of fields to describe authentication and
2//! authorization schemes, flows, and scopes required to access
3//! [Assets](stac::Asset) and [Links](stac::Link) that align with the [OpenAPI
4//! security spec](https://swagger.io/docs/specification/authentication/).
5
6use crate::Extension;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use std::collections::HashMap;
10
11/// The authentication extension fields.
12#[derive(Debug, Serialize, Deserialize)]
13pub struct Authentication {
14 /// A property that contains all of the [scheme definitions](Scheme) used by
15 /// [Assets](stac::Asset) and [Links](stac::Link) in the STAC [Item](crate::Item) or [Collection](crate::Collection).
16 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
17 pub schemes: HashMap<String, Scheme>,
18
19 /// A property that specifies which schemes may be used to access an [Asset](stac::Asset)
20 /// or [Link](stac::Link).
21 #[serde(default, skip_serializing_if = "Vec::is_empty")]
22 pub refs: Vec<String>,
23}
24
25/// The Authentication Scheme extends the [OpenAPI security
26/// spec](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object)
27/// for support of OAuth2.0, API Key, and OpenID Connect authentication.
28#[derive(Debug, Serialize, Deserialize)]
29pub struct Scheme {
30 /// The authentication scheme type used to access the data (`http` | `s3` |
31 /// `signedUrl` | `oauth2` | `apiKey` | `openIdConnect` | a custom scheme type ).
32 pub r#type: String,
33
34 /// Additional instructions for authentication.
35 ///
36 /// [CommonMark 0.29](https://commonmark.org/) syntax MAY be used for rich text representation.
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub description: Option<String>,
39
40 /// The name of the header, query, or cookie parameter to be used.
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub name: Option<String>,
43
44 /// The location of the API key (`query` | `header` | `cookie`).
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub r#in: Option<In>,
47
48 /// The name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC7235.
49 ///
50 /// The values used SHOULD be registered in the IANA Authentication Scheme registry.
51 /// (`basic` | `bearer` | `digest` | `dpop` | `hoba` | `mutual` |
52 /// `negotiate` | `oauth` (1.0) | `privatetoken` | `scram-sha-1` |
53 /// `scram-sha-256` | `vapid`)
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub scheme: Option<String>,
56
57 /// Scenarios an API client performs to get an access token from the authorization server.
58 ///
59 /// For oauth2 the following keys are pre-defined for the corresponding
60 /// OAuth flows: `authorizationCode` | `implicit` | `password` |
61 /// `clientCredentials`. The OAuth2 Flow Object applies for oauth2, the
62 /// Signed URL Object applies to signedUrl.
63 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
64 pub flows: HashMap<String, Flow>,
65
66 /// OpenID Connect URL to discover OpenID configuration values.
67 ///
68 /// This MUST be in the form of a URL.
69 #[serde(skip_serializing_if = "Option::is_none", rename = "openIdConnectUrl")]
70 pub open_id_connect_url: Option<String>,
71}
72
73/// The OAuth2 Flow Object applies for oauth2, the Signed URL Object applies to signedUrl.
74#[derive(Debug, Serialize, Deserialize)]
75#[serde(untagged)]
76pub enum Flow {
77 /// Based on the [OpenAPI OAuth Flow
78 /// Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object).
79 ///
80 /// Allows configuration of the supported OAuth Flows.
81 OAuth2 {
82 /// The authorization URL to be used for this flow.
83 ///
84 /// This MUST be in the form of a URL.
85 #[serde(skip_serializing_if = "Option::is_none", rename = "authorizationUrl")]
86 authorization_url: Option<String>,
87
88 /// The token URL to be used for this flow.
89 ///
90 /// This MUST be in the form of a URL.
91 #[serde(skip_serializing_if = "Option::is_none", rename = "tokenUrl")]
92 token_url: Option<String>,
93
94 /// The available scopes for the authentication scheme.
95 ///
96 /// A map between the scope name and a short description for it. The map MAY be empty.
97 scopes: HashMap<String, String>,
98
99 /// The URL to be used for obtaining refresh tokens.
100 ///
101 /// This MUST be in the form of a URL.
102 #[serde(skip_serializing_if = "Option::is_none", rename = "refreshUrl")]
103 refresh_url: Option<String>,
104 },
105
106 /// A signed url flow.
107 SignedUrl {
108 /// The method to be used for requests.
109 method: String,
110
111 /// The signed URL API endpoint to be used for this flow.
112 ///
113 /// If not inferred from the client environment, this must be defined in the authentication flow.
114 #[serde(skip_serializing_if = "Option::is_none", rename = "authorizationApi")]
115 authorization_api: Option<String>,
116
117 /// Parameter definition for requests to the authorizationApi
118 #[serde(skip_serializing_if = "HashMap::is_empty")]
119 parameters: HashMap<String, Parameter>,
120
121 /// Key name for the signed URL field in an authorizationApi response
122 #[serde(skip_serializing_if = "Option::is_none", rename = "responseField")]
123 response_field: Option<String>,
124 },
125}
126
127/// Definition for a request parameter.
128#[derive(Debug, Serialize, Deserialize)]
129pub struct Parameter {
130 /// The location of the parameter (`query` | `header` | `body`).
131 pub r#in: String,
132
133 /// Setting for optional or required parameter.
134 pub required: bool,
135
136 /// Plain language description of the parameter
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub description: Option<String>,
139
140 /// Schema object following the [JSON Schema draft-07](Schema object following the JSON Schema draft-07).
141 pub schema: HashMap<String, Value>,
142}
143
144/// Query, header, or cookie.
145#[derive(Debug, Serialize, Deserialize, Default, PartialEq)]
146pub enum In {
147 /// In the GET query string.
148 #[serde(rename = "query")]
149 Query,
150
151 /// In the headers.
152 #[default]
153 #[serde(rename = "header")]
154 Header,
155
156 /// In the cookie.
157 #[serde(rename = "cookie")]
158 Cookie,
159}
160
161impl Extension for Authentication {
162 const IDENTIFIER: &'static str =
163 "https://stac-extensions.github.io/authentication/v1.1.0/schema.json";
164 const PREFIX: &'static str = "auth";
165}
166
167#[cfg(test)]
168mod tests {
169 use super::{Authentication, In, Scheme};
170 use crate::{Collection, Extensions, Item};
171 use serde_json::json;
172
173 #[test]
174 fn collection() {
175 let collection: Collection = stac::read("data/auth/collection.json").unwrap();
176 let authentication: Authentication = collection.extension().unwrap();
177 let oauth = authentication.schemes.get("oauth").unwrap();
178 let _ = oauth.flows.get("authorizationCode").unwrap();
179 }
180
181 #[test]
182 fn item() {
183 let collection: Item = stac::read("data/auth/item.json").unwrap();
184 let authentication: Authentication = collection.extension().unwrap();
185 let _ = authentication.schemes.get("none").unwrap();
186 }
187
188 #[test]
189 fn api_key() {
190 let scheme: Scheme = serde_json::from_value(json!({
191 "type": "apiKey",
192 "in": "query",
193 "name": "API_KEY"
194 }))
195 .unwrap();
196 assert_eq!(scheme.r#in.unwrap(), In::Query);
197 }
198}